Wîkîferheng kuwiktionary https://ku.wiktionary.org/wiki/Destp%C3%AAk MediaWiki 1.47.0-wmf.12 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 Juli 0 57771 6291626 6117930 2026-07-27T06:18:21Z Jeuwre 4694 Almanî /* Bilêvkirin */ 6291626 wikitext text/x-wiki == {{ziman|nds-de}} == === Navdêr === {{navdêr|nds-de|z=n}} # {{mijar|nds-de|mehên salê}} {{g|ku|tîrmeh}}, {{g|ku|temûz}} {{şerh|meha salê ya heftan}} === Çavkanî === {{rûpela wergerê|nds-de|tîrmeh}} == {{ziman|de}} == === Bilêvkirin === * {{IPA|de|[ˈjuːli]}} * {{deng|de|De-Juli2.ogg|Deng|dever=Berlîn, Almanya}} * {{deng|de|De-Juli.ogg|Deng}} * {{deng|de|LL-Q188 (deu)-Sebastian Wallroth-Juli.wav|Deng|dever=Berlîn, Almanya}} * {{deng|de|LL-Q188 (deu)-Natschoba-Juli.wav|Deng|dever=Almanya}} * {{kîte|de|Ju|li}} === Navdêr === {{navdêr|de|z=n}} # [[tîrmeh]] == {{ziman|stq}} == === Navdêr === {{navdêr|stq}} # {{mijar|stq|mehên salê}} {{g|ku|tîrmeh}}, {{g|ku|temûz}} {{şerh|meha salê ya heftan}} === Çavkanî === {{rûpela wergerê|stq|tîrmeh}} == {{ziman|li}} == === Navdêr === {{navdêr|li}} # {{mijar|li|mehên salê}} {{g|ku|tîrmeh}}, {{g|ku|temûz}} {{şerh|meha salê ya heftan}} === Çavkanî === {{rûpela wergerê|li|tîrmeh}} == {{ziman|lb}} == === Navdêr === {{navdêr|lb|z=n}} # {{mijar|lb|mehên salê}} {{g|ku|tîrmeh}}, {{g|ku|temûz}} {{şerh|meha salê ya heftan}} === Çavkanî === {{rûpela wergerê|lb|tîrmeh}} == {{ziman|pdt}} == === Navdêr === {{navdêr|pdt|z=n}} # {{mijar|pdt|mehên salê}} {{g|ku|tîrmeh}}, {{g|ku|temûz}} {{şerh|meha salê ya heftan}} === Çavkanî === {{rûpela wergerê|pdt|tîrmeh}} 0iqxesgnco7h33llwe46vs00lbxhncn MediaWiki:Newarticletext 8 118589 6291597 6111483 2026-07-26T20:06:51Z Wikihez 7344 6291597 wikitext text/x-wiki {{#switch:{{NAMESPACENUMBER}}<!-- -->|14= {{tabloya kategorîkirinê}}<!-- -->|#default=<div class="plainlinks" style="background: var(--wikt-palette-paleblue, #F9F9F9); border: 1px solid var(--wikt-palette-lightgrey, #aaa); padding-left: 12px; margin-bottom: 5px"> '''Ev rûpel hîn tine.''' * Eger tu bixwazî vê rûpelê çêkî, dest bi nivîsandinê bike û piştre qeyd bike. '''Wêrek be''', biceribîne!<br /> * Ji bo alîkariyê binêre: [[{{MediaWiki:Helppage}}|Alîkarî]].<br /> * Heke tu bi xeletî hatî, vegere rûpela berê. </div> }} 1s52zmm3s8gkv8ks8e3l9qxgg5xgj6v 6291598 6291597 2026-07-26T20:07:46Z Wikihez 7344 6291598 wikitext text/x-wiki {{#switch:{{NAMESPACENUMBER}}<!-- -->|14= {{tabloya kategorîkirinê}}<!-- -->|#default=<div class="plainlinks" style="background: var(--wikt-palette-paleblue, #F9F9F9); border: 1px solid var(--wikt-palette-lightgrey, #aaa); color: var(--wikt-palette-black, #202122); padding-left: 12px; margin-bottom: 5px"> '''Ev rûpel hîn tine.''' * Eger tu bixwazî vê rûpelê çêkî, dest bi nivîsandinê bike û piştre qeyd bike. '''Wêrek be''', biceribîne!<br /> * Ji bo alîkariyê binêre: [[{{MediaWiki:Helppage}}|Alîkarî]].<br /> * Heke tu bi xeletî hatî, vegere rûpela berê. </div> }} 7uoa5zkcpc8wv7a9pz9hf7xvlfnstj9 anafor 0 122516 6291561 4605196 2026-07-26T13:34:15Z Wikihez 7344 /* Navdêr */ 6291561 wikitext text/x-wiki == {{ziman|tr}} == === Navdêr === {{navdêr|tr}} # [[gêrav]],[[gerînek]], [[ger]],[[gêreba]] # {{e|tr|argo}} [[tişt|tiştê]] ku [[bêked|bêked]] û [[hed|hed]] û [[bi awayekî]] [[nelirê|nelirê]] [[hatin|hatiye]] [[bidestxistin]] svql23bb18sxg6sta3bshd8rlksjxb5 hematolojî 0 296512 6291623 6282292 2026-07-26T23:41:49Z Wikihez 7344 6291623 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-kîte}} === Navdêr === {{ku-tewîn-nav|hematolojî|mê}} {{navdêr|ku|z=m}} # [[xwînnasî]], [[vekolîn]]ên [[bijîşkî]] li ser [[xwîn]]a [[nexweş]]an #* {{jêder|ku|jêgirtin=Di encama pêşbirkê de pêşniyara kilînîka '''hematolojiyê''' ya "Agahdariya pirzimanî û mûzîk" serkeftî bû.|www.aa.com.tr/kk 10/2013}} ==== Formên alternatîv ==== * [[hêmatolojî]] === Têkilîdar === * [[hematolog]] * [[hemoglobîn]] ==== Etîmolojî ==== {{bêetîmolojî|ku}} ==== Werger ==== {{werger-ser}} * {{Z|af}}: {{W-|af|hematologie}} * {{Z|sq}}: {{W-|sq|hematologjia|m}} * {{Z|de}}: {{W-|de|Hämatologie|m}} * {{Z|ast}}: {{W-|ast|hematoloxía|m}} * {{Z|az}}: {{W-|az|hematologiya}} * {{Z|eu}}: {{W+|eu|hematologia}} * {{Z|ba}}: {{W-|ba|гематология}} * {{Z|be}}: {{W-|be|гематалогія|m}} * {{Z|bg}}: {{W-|bg|хематология|m}} * {{Z|cs}}: {{W+|cs|hematologie|m}} * {{Z|zh}}: *: {{Z|yue}}: {{W-|yue|血液學|tr=hyut3 jik6 hok6}} *: {{Z|cmn}}: {{W-|cmn|血液學|tr=xuèyèxué}} * {{Z|da}}: {{W-|da|hæmatologi|g}} * {{Z|id}}: {{W+|id|hematologi}} * {{Z|ar}}: {{W-|ar|عِلْم الدَّمْ}} * {{Z|hy}}: {{W+|hy|արյունաբանություն}} * {{Z|eo}}: {{W-|eo|hematologio}} * {{Z|fa}}: {{W-|fa|خون‌شناسی|tr=xun-šenâsi}}, {{W-|fa|هماتولوژی|tr=hemâtoloži}} * {{Z|fi}}: {{W+|fi|hematologia}}, {{W+|fi|veritautioppi}} * {{Z|fr}}: {{W+|fr|hématologie|m}} * {{Z|gl}}: {{W-|gl|hematoloxía|m}} * {{Z|ka}}: {{W-|ka|ჰემატოლოგია}} * {{Z|hi}}: {{W-|hi|रक्तशास्त्र}} * {{Z|nl}}: {{W+|nl|hematologie|m}} * {{Z|io}}: {{W+|io|hematologio}} * {{Z|en}}: {{W+|en|haematology}} * {{Z|ga}}: {{W-|ga|haemaiteolaíocht|m}} * {{Z|it}}: {{W+|it|ematologia|m}} * {{Z|is}}: {{W+|is|blóðfræði|m}} * {{Z|ja}}: {{W-|ja|血液学|tr=けつえきがく, ketsuekigaku|sc=Jpan}} * {{Z|jv}}: {{W-|jv|hematologi}} * {{Z|ca}}: {{W+|ca|hematologia|m}} * {{Z|ky}}: {{W-|ky|гематология}} * {{Z|ko}}: {{W+|ko|혈액학|sc=Kore}} * {{Z|la}}: {{W-|la|haematologia|m}} * {{Z|lv}}: {{W-|lv|hematoloģija|m}} * {{Z|li}}: {{W+|li|bloodslieër|m}} * {{Z|lt}}: {{W-|lt|hematologija|m}} * {{Z|mk}}: {{W-|mk|хематологија|m}} * {{Z|ml}}: {{W-|ml|ഹീമറ്റോളജി}} * {{Z|ms}}: {{W+|ms|hematologi}}, {{W-|ms|kaji darah}} * {{Z|mi}}: {{W-|mi|mātai toto}} * {{Z|hu}}: {{W+|hu|hematológia}} * {{Z|nds}}: *: {{Z|nds-de}}: {{W-|nds-de|Hämatologie|m}} * {{Z|new}}: {{W-|new|हेमाटोलोजी}} * {{Z|no}}: *: {{Z|nb}}: {{W-|nb|hematologi|n}} *: {{Z|nn}}: {{W-|nn|hematologi|n}} * {{Z|oc}}: {{W-|oc|ematologia|m}} * {{Z|pl}}: {{W+|pl|hematologia|m}} * {{Z|pt}}: {{W+|pt|hematologia|m}} * {{Z|kk}}: {{W-|kk|гематология}} * {{Z|ro}}: {{W+|ro|hematologie|m}} * {{Z|ru}}: {{W+|ru|гематоло́гия|m}} * {{Z|sa}}: {{W-|sa|रक्तशास्त्रम्}} * {{Z|sh}}: *: Kirîlî: {{W-|sh|хематоло̀гија|m}} *: Latînî: {{W+|sh|hematològija|m}} * {{Z|sk}}: {{W+|sk|hematológia|m}} * {{Z|sl}}: {{W+|sl|hematologija|m}} * Sorbî: *: {{Z|dsb}}: {{W-|dsb|hematologija|m}} *: {{Z|hsb}}: {{W-|hsb|hematologija|m}} * {{Z|es}}: {{W+|es|hematología|m}} * {{Z|sv}}: {{W-|sv|hematologi|g}} * {{Z|tg}}: {{W+|tg|хуншиносӣ}} * {{Z|tl}}: {{W-|tl|paladuguan}}, {{W-|tl|hematolohiya}} * {{Z|th}}: {{W-|th|โลหิตวิทยา|tr=loh-hìt-wít-tá-yaa}} * {{Z|tt}}: {{W-|tt|гематология}} * {{Z|tr}}: {{W+|tr|kan bilimi}}, {{W+|tr|hematoloji}} * {{Z|uk}}: {{W-|uk|гематологія|m}} * Uzbek: *: Kirîlî: {{W+|uz|гематология}} *: Latînî: {{W+|uz|gematologiya}} * {{Z|vi}}: {{W+|vi|huyết học}} * {{Z|war}}: {{W-|war|hematolohiya}} * {{Z|cy}}: {{W-|cy|haematoleg|m}} * {{Z|el}}: {{W+|el|αιματολογία|m}} {{werger-bin}} :{{werger çavkanî|en|haematology|beş=Ev qismê Wergerê|oldid=75897409}} 74ds7s9xu5klzs6pq5zcan5qotby6bk bowl 0 362589 6291569 6092967 2026-07-26T15:14:23Z Wikihez 7344 /* Navdêr */ 6291569 wikitext text/x-wiki == {{ziman|en}} == [[Wêne:Bowl MET DP243440 (cropped).jpg|thumb|Kaseyeke çînî ya Misra Kevnare, teqrîben ji salên 200–150 BM]] [[Wêne:Bowl (jatte à anses relevées or jatte écuelle) MET DP169254 (cropped).jpg|thumb|Kaseyeke neoklasîk ya fransî (jatte à anses relevées or jatte écuelle), teqrîben ji salên 1787-1788]] === Bilêvkirin === * {{mj|KY}} {{IPA|en|/bəʊl/|[bɒʊɫ]}} * {{mj|NZ|Awistralya}} {{IPA|en|/bɐʉl/|[bɒʊɫ]}} * {{mj|DYA}} {{IPA|en|/boʊl/|[boɫ]}} * {{deng|en|en-us-bowl.ogg|dever=DYA}} * {{deng|en|LL-Q1860 (eng)-Nattes à chat-bowl.wav|Deng|dever=}} * {{deng|en|LL-Q1860 (eng)-Samuel Muldoon (Muldoonsamuel55)-bowl.wav|Deng|dever=Aurora, DYA}} === Navdêr === {{en-navdêr}} # [[kasik]], [[kase]] {{îzah|[[qab|qabeke]] [[xwarin|xwarinê]] yê bi [[şikl|şiklê]] [[nîv|nîv]]-[[kure]] yê ku [[ji bo]] [[xwarin|xwarinan]] [[girtin|bigire]] [[nav|nava]] [[xwe|xwe]], ji bo [[tevlihevkirin|tevlihevkirina]] xwarinan an ji bo [[servîskirin|servîskirina]] wan tê [[bikaranîn]], [[wekî|wekî]] [[nimûne|nimûne]] [[salate|salate]], [[fêkî|fêkî]] an [[şorbe|şorbe]] an [[tişt|tiştên]] [[din|din]] [[xistin|dixine]] [[navê|navê]]}} # Bi [[miqdar|miqdara]] [[kase|kaseyekê]]. #: {{hevmane|en|bowlful}} #: {{mk|en|You can’t have any more soup – you’ve had three '''bowls''' already.|Tu nikarî êdî zêdetir şorbe vexwî, te jixwe bi qasî sê kaseyan vexwariye.}} # {{f|en|çêkirina xwarinê}} [[xwarin|Xwarineke]] ku ji [[têkilkirin|têkilkirina]] [[çend|çend]] [[xwarin|xwarinên]] [[cihê]] [[pêk hatin|pêk tê]], ku ne [[hewce|hewceyê]] [[temamî|temamiya]] wan were [[pijandin]], û di kaseyekê de tên [[servîs kirin|servîskirin]]. # [[tiraş|Tiraş]]eke [[por]]ê yê ku porên [[rast|rast]] ji [[kinar|kinara]] bi [[eynî]] [[dirêjahî|dirêjahiyê]] tên [[qusandin]] û şiklê kaseyekê [[derhatin|dertê]] holê. #: {{hevmaneyên peyvê|en|bowl cut|pudding bowl}} # [[qism|Qismê]] [[vala]] yê [[gilover]] yê her tiştekî. #: {{mk|en|Direct the cleaning fluid around the toilet '''bowl''' and under the rim.|Aliyê şilaviya paqijkirinê bidin ber bi derdora qulika tiwaletê û binê kinarê.}} ## Qismê [[kevçî|kevçiyê]] yê ku tiştan [[hilgirtin|hildigire]], [[zidd|ziddê]] qismê [[destik|destikê]] (ku ti tiştî [[girtin|nagire]] nava xwe). ## Qismeke [[qelûn]]ê an [[nargîle]]yê ku ji bo [[kêşan|kêşanê]] bi [[marijuana]] ([[çeşîd|çeşîdeke]] [[esrar|esrarê]]) hatiye tijîkirin. ## {{f|en|tîpografî}} [[parçe|Parçeyek]] [[gilover|gilover]] a [[glîf|glîfê]] ku [[cih|cihê]] [[vala|vala]] [[peçandin|dipêçe]], wekî di [[herf|herfên]] "d" û "o"yê de. # {{f|en|landforms}} [[krater|Krater]]eke gilover (an wekî wê) yê di [[zemîn|zemînê]] de. #: {{hevmaneyên peyvê|en|crater|hollow}} # {{f|en|spor|tiyatro}} [[stadyum|Stadyûm]] an [[amfîtiyatro|amfîtiyatroyeke]] bi şiklê [[elîpsê|elîpsê]] yê ku kaseyê tîna bîra mirov. # {{f|en|futbola amerîkî}} [[pêşbazî|Pêşbaziyek]] [[futbol|futbolê]] yê [[piştî]] [[sezon|sezonê]] (wekî nimûne ''Rose Bowl'', ''Super Bowl'') ==== Jê ==== {{kol3|en |begging bowl |[[bowl cut]] |bowl game |bowl lute |coffee bowl |dustbowl |fruit bowl |mixing bowl |pudding bowl |salad bowl |singing bowl |soup bowl |toilet bowl |bowl barrow|bowl chair|bowl hat|bowl of cherries|bowl pack|bowl wax|Buddha bowl|clean bowl|doggy dinner bowl look|finger bowl|fish bowl|food bowl|goldfish bowl|iron rice bowl|lawn-bowl|mazer bowl|poké bowl|pudding-bowl|punch bowl|punch bowl waterfall|quiz bowl|rice bowl|scorpion bowl|shampoo bowl|shit bowl|shit-bowl|slop bowl|slop-bowl|spaghetti bowl|spoon bowl|spoon-bowl|sugar bowl|swill-bowl|take the browns to the super bowl|tea bowl|track and bowl system|volcano bowl|work bowl|Zuni bowl}} ==== Etîmolojî ==== Ji {{mîr|enm|en|bolle}}, ji {{mîr|ang|en|bolla}}, {{qal|ang|bolle||kase}}, ji {{mîr|gmw-pro|en|*bollā}}, ji {{mîr|gem-pro|en|*bullǭ||top, amana gilover}}. Bide ber {{hv|frr|bol}}, {{hv|gml|bolle}}, {{qal|gml|bole||objeya gilover}}, {{hv|nl|bol||top, kure}}, {{hv|de|Bolle}}, {{hv|da|bolle}}, {{hv|is|bolli}}. {{cotreh|en|boule|pulla}}. ==== Dûnde ==== * {{dû|bi|bol}} * {{dû|eo|bovlo|deyn=1}} * {{dû|fr|bol|deyn=1}} * {{dû|de|Bowle|Bowl|deyn=1}} ** {{dû|hu|bólé|deyn=1}} * {{dû|io|bolo|deyn=1}} * {{dû|ga|bolla|deyn=1}} * {{dû|ja|ボール|tr=bōru|deyn=1}} * {{dû|lv|bole|deyn=1}} {{mj|muhtemelen ji almanî}} * {{dû|ko|볼|deyn=1}} * {{dû|gd|bòl|deyn=1}} * {{dû|sv|bål|deyn=1}} === Navdêr === {{en-navdêr}} # [[gog|Gog]]a ku di lîstika ''lawn bowls''-ê de ji aliyê lîstikvanan ve tê gindirandin. # {{f|en|di pirjimariyê de|lê bi lêkereke yekjimar tê bikaranîn}} Lîstikek e. #: {{hevmaneyên peyvê|en|lawn bowls|lawn bowling}} ==== Jê ==== {{kol2|en|bowl-off|bowl-out}} ==== Etîmolojî ==== Ji {{mîr|enm|en|bowle}}, {{qal|enm|boule}}, ji {{ji|fro|en|boule||ball}}, ji {{ji|la|en|bulla||bubble, stud, round object}}. {{cotreh|en|poll}}. === Lêker === {{en-lêker}} # {{f|en|gh}} Bi şikleke rast topê [[gindirandin]] an [[avêtin]], di lîstika krîketê de an di lîstik û sporên mîna wê de. # {{f|en|ngh}} Ji bo avêtina topê (di krîketê de û di lîstik û sporên wekî wê de). # {{f|en|ngh}} Lîstina bowlîngê an lîstikeke wek wê. # Bi rehetî û hêsanî bar kirin an gindirandin, wekî ku li ser tekerekê be. #: {{mk|en|We were '''bowled''' rapidly along the road.|Em bi lez û bez li ser rê gindirîbûn.}} # Li tişekî dan ku digere an digindire. ==== Jê ==== {{kol3|en |bowl a googly| |bowl along |bowler |bowling |bowling alley |bowling ball |bowl out |bowl over }} ==== Dûnde ==== * {{dû|da|bowle|deyn=1}} * {{dû|nl|bowlen|deyn=1}} * {{dû|de|bowlen|deyn=1}} ==== Anagram ==== * {{anagram|en|a=blow|Blow|b'low|blow}} 8b0hnnx3ep86op9vo3wy1qzed55hpys 6291570 6291569 2026-07-26T15:14:40Z Wikihez 7344 /* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re 6291570 wikitext text/x-wiki == {{ziman|en}} == [[Wêne:Bowl MET DP243440 (cropped).jpg|thumb|Kaseyeke çînî ya Misra Kevnare, teqrîben ji salên 200–150 BM]] [[Wêne:Bowl (jatte à anses relevées or jatte écuelle) MET DP169254 (cropped).jpg|thumb|Kaseyeke neoklasîk ya fransî (jatte à anses relevées or jatte écuelle), teqrîben ji salên 1787-1788]] === Bilêvkirin === * {{mj|KY}} {{IPA|en|/bəʊl/|[bɒʊɫ]}} * {{mj|NZ|Awistralya}} {{IPA|en|/bɐʉl/|[bɒʊɫ]}} * {{mj|DYA}} {{IPA|en|/boʊl/|[boɫ]}} * {{deng|en|en-us-bowl.ogg|dever=DYA}} * {{deng|en|LL-Q1860 (eng)-Nattes à chat-bowl.wav}} * {{deng|en|LL-Q1860 (eng)-Samuel Muldoon (Muldoonsamuel55)-bowl.wav|dever=Aurora, DYA}} === Navdêr === {{en-navdêr}} # [[kasik]], [[kase]] {{îzah|[[qab|qabeke]] [[xwarin|xwarinê]] yê bi [[şikl|şiklê]] [[nîv|nîv]]-[[kure]] yê ku [[ji bo]] [[xwarin|xwarinan]] [[girtin|bigire]] [[nav|nava]] [[xwe|xwe]], ji bo [[tevlihevkirin|tevlihevkirina]] xwarinan an ji bo [[servîskirin|servîskirina]] wan tê [[bikaranîn]], [[wekî|wekî]] [[nimûne|nimûne]] [[salate|salate]], [[fêkî|fêkî]] an [[şorbe|şorbe]] an [[tişt|tiştên]] [[din|din]] [[xistin|dixine]] [[navê|navê]]}} # Bi [[miqdar|miqdara]] [[kase|kaseyekê]]. #: {{hevmane|en|bowlful}} #: {{mk|en|You can’t have any more soup – you’ve had three '''bowls''' already.|Tu nikarî êdî zêdetir şorbe vexwî, te jixwe bi qasî sê kaseyan vexwariye.}} # {{f|en|çêkirina xwarinê}} [[xwarin|Xwarineke]] ku ji [[têkilkirin|têkilkirina]] [[çend|çend]] [[xwarin|xwarinên]] [[cihê]] [[pêk hatin|pêk tê]], ku ne [[hewce|hewceyê]] [[temamî|temamiya]] wan were [[pijandin]], û di kaseyekê de tên [[servîs kirin|servîskirin]]. # [[tiraş|Tiraş]]eke [[por]]ê yê ku porên [[rast|rast]] ji [[kinar|kinara]] bi [[eynî]] [[dirêjahî|dirêjahiyê]] tên [[qusandin]] û şiklê kaseyekê [[derhatin|dertê]] holê. #: {{hevmaneyên peyvê|en|bowl cut|pudding bowl}} # [[qism|Qismê]] [[vala]] yê [[gilover]] yê her tiştekî. #: {{mk|en|Direct the cleaning fluid around the toilet '''bowl''' and under the rim.|Aliyê şilaviya paqijkirinê bidin ber bi derdora qulika tiwaletê û binê kinarê.}} ## Qismê [[kevçî|kevçiyê]] yê ku tiştan [[hilgirtin|hildigire]], [[zidd|ziddê]] qismê [[destik|destikê]] (ku ti tiştî [[girtin|nagire]] nava xwe). ## Qismeke [[qelûn]]ê an [[nargîle]]yê ku ji bo [[kêşan|kêşanê]] bi [[marijuana]] ([[çeşîd|çeşîdeke]] [[esrar|esrarê]]) hatiye tijîkirin. ## {{f|en|tîpografî}} [[parçe|Parçeyek]] [[gilover|gilover]] a [[glîf|glîfê]] ku [[cih|cihê]] [[vala|vala]] [[peçandin|dipêçe]], wekî di [[herf|herfên]] "d" û "o"yê de. # {{f|en|landforms}} [[krater|Krater]]eke gilover (an wekî wê) yê di [[zemîn|zemînê]] de. #: {{hevmaneyên peyvê|en|crater|hollow}} # {{f|en|spor|tiyatro}} [[stadyum|Stadyûm]] an [[amfîtiyatro|amfîtiyatroyeke]] bi şiklê [[elîpsê|elîpsê]] yê ku kaseyê tîna bîra mirov. # {{f|en|futbola amerîkî}} [[pêşbazî|Pêşbaziyek]] [[futbol|futbolê]] yê [[piştî]] [[sezon|sezonê]] (wekî nimûne ''Rose Bowl'', ''Super Bowl'') ==== Jê ==== {{kol3|en |begging bowl |[[bowl cut]] |bowl game |bowl lute |coffee bowl |dustbowl |fruit bowl |mixing bowl |pudding bowl |salad bowl |singing bowl |soup bowl |toilet bowl |bowl barrow|bowl chair|bowl hat|bowl of cherries|bowl pack|bowl wax|Buddha bowl|clean bowl|doggy dinner bowl look|finger bowl|fish bowl|food bowl|goldfish bowl|iron rice bowl|lawn-bowl|mazer bowl|poké bowl|pudding-bowl|punch bowl|punch bowl waterfall|quiz bowl|rice bowl|scorpion bowl|shampoo bowl|shit bowl|shit-bowl|slop bowl|slop-bowl|spaghetti bowl|spoon bowl|spoon-bowl|sugar bowl|swill-bowl|take the browns to the super bowl|tea bowl|track and bowl system|volcano bowl|work bowl|Zuni bowl}} ==== Etîmolojî ==== Ji {{mîr|enm|en|bolle}}, ji {{mîr|ang|en|bolla}}, {{qal|ang|bolle||kase}}, ji {{mîr|gmw-pro|en|*bollā}}, ji {{mîr|gem-pro|en|*bullǭ||top, amana gilover}}. Bide ber {{hv|frr|bol}}, {{hv|gml|bolle}}, {{qal|gml|bole||objeya gilover}}, {{hv|nl|bol||top, kure}}, {{hv|de|Bolle}}, {{hv|da|bolle}}, {{hv|is|bolli}}. {{cotreh|en|boule|pulla}}. ==== Dûnde ==== * {{dû|bi|bol}} * {{dû|eo|bovlo|deyn=1}} * {{dû|fr|bol|deyn=1}} * {{dû|de|Bowle|Bowl|deyn=1}} ** {{dû|hu|bólé|deyn=1}} * {{dû|io|bolo|deyn=1}} * {{dû|ga|bolla|deyn=1}} * {{dû|ja|ボール|tr=bōru|deyn=1}} * {{dû|lv|bole|deyn=1}} {{mj|muhtemelen ji almanî}} * {{dû|ko|볼|deyn=1}} * {{dû|gd|bòl|deyn=1}} * {{dû|sv|bål|deyn=1}} === Navdêr === {{en-navdêr}} # [[gog|Gog]]a ku di lîstika ''lawn bowls''-ê de ji aliyê lîstikvanan ve tê gindirandin. # {{f|en|di pirjimariyê de|lê bi lêkereke yekjimar tê bikaranîn}} Lîstikek e. #: {{hevmaneyên peyvê|en|lawn bowls|lawn bowling}} ==== Jê ==== {{kol2|en|bowl-off|bowl-out}} ==== Etîmolojî ==== Ji {{mîr|enm|en|bowle}}, {{qal|enm|boule}}, ji {{ji|fro|en|boule||ball}}, ji {{ji|la|en|bulla||bubble, stud, round object}}. {{cotreh|en|poll}}. === Lêker === {{en-lêker}} # {{f|en|gh}} Bi şikleke rast topê [[gindirandin]] an [[avêtin]], di lîstika krîketê de an di lîstik û sporên mîna wê de. # {{f|en|ngh}} Ji bo avêtina topê (di krîketê de û di lîstik û sporên wekî wê de). # {{f|en|ngh}} Lîstina bowlîngê an lîstikeke wek wê. # Bi rehetî û hêsanî bar kirin an gindirandin, wekî ku li ser tekerekê be. #: {{mk|en|We were '''bowled''' rapidly along the road.|Em bi lez û bez li ser rê gindirîbûn.}} # Li tişekî dan ku digere an digindire. ==== Jê ==== {{kol3|en |bowl a googly| |bowl along |bowler |bowling |bowling alley |bowling ball |bowl out |bowl over }} ==== Dûnde ==== * {{dû|da|bowle|deyn=1}} * {{dû|nl|bowlen|deyn=1}} * {{dû|de|bowlen|deyn=1}} ==== Anagram ==== * {{anagram|en|a=blow|Blow|b'low|blow}} sft22f829eq076i85u2w9g0w5xzyy9t 6291571 6291570 2026-07-26T15:17:10Z Wikihez 7344 /* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re 6291571 wikitext text/x-wiki == {{ziman|en}} == [[File:Bowl MET DP243440 (cropped).jpg|thumb|{{zim|en|Ancient Egyptian faience bowl, from circa 200–150 BC}} Kaseyeke çînî ya Misra Kevnare, teqrîben ji salên 200–150 BM]] [[File:Bowl (jatte à anses relevées or jatte écuelle) MET DP169254 (cropped).jpg|thumb|{{zim|en|French Neoclassical bowl (jatte à anses relevées or jatte écuelle), from circa 1787-1788}} Kaseyeke neoklasîk ya fransî (jatte à anses relevées or jatte écuelle), teqrîben ji salên 1787-1788]] === Bilêvkirin === * {{mj|KY}} {{IPA|en|/bəʊl/|[bɒʊɫ]}} * {{mj|NZ|Awistralya}} {{IPA|en|/bɐʉl/|[bɒʊɫ]}} * {{mj|DYA}} {{IPA|en|/boʊl/|[boɫ]}} * {{deng|en|en-us-bowl.ogg|dever=DYA}} * {{deng|en|LL-Q1860 (eng)-Nattes à chat-bowl.wav}} * {{deng|en|LL-Q1860 (eng)-Samuel Muldoon (Muldoonsamuel55)-bowl.wav|dever=Aurora, DYA}} === Navdêr === {{en-navdêr}} # [[kasik]], [[kase]] {{îzah|[[qab|qabeke]] [[xwarin|xwarinê]] yê bi [[şikl|şiklê]] [[nîv|nîv]]-[[kure]] yê ku [[ji bo]] [[xwarin|xwarinan]] [[girtin|bigire]] [[nav|nava]] [[xwe|xwe]], ji bo [[tevlihevkirin|tevlihevkirina]] xwarinan an ji bo [[servîskirin|servîskirina]] wan tê [[bikaranîn]], [[wekî|wekî]] [[nimûne|nimûne]] [[salate|salate]], [[fêkî|fêkî]] an [[şorbe|şorbe]] an [[tişt|tiştên]] [[din|din]] [[xistin|dixine]] [[navê|navê]]}} # Bi [[miqdar|miqdara]] [[kase|kaseyekê]]. #: {{hevmane|en|bowlful}} #: {{mk|en|You can’t have any more soup – you’ve had three '''bowls''' already.|Tu nikarî êdî zêdetir şorbe vexwî, te jixwe bi qasî sê kaseyan vexwariye.}} # {{f|en|çêkirina xwarinê}} [[xwarin|Xwarineke]] ku ji [[têkilkirin|têkilkirina]] [[çend|çend]] [[xwarin|xwarinên]] [[cihê]] [[pêk hatin|pêk tê]], ku ne [[hewce|hewceyê]] [[temamî|temamiya]] wan were [[pijandin]], û di kaseyekê de tên [[servîs kirin|servîskirin]]. # [[tiraş|Tiraş]]eke [[por]]ê yê ku porên [[rast|rast]] ji [[kinar|kinara]] bi [[eynî]] [[dirêjahî|dirêjahiyê]] tên [[qusandin]] û şiklê kaseyekê [[derhatin|dertê]] holê. #: {{hevmaneyên peyvê|en|bowl cut|pudding bowl}} # [[qism|Qismê]] [[vala]] yê [[gilover]] yê her tiştekî. #: {{mk|en|Direct the cleaning fluid around the toilet '''bowl''' and under the rim.|Aliyê şilaviya paqijkirinê bidin ber bi derdora qulika tiwaletê û binê kinarê.}} ## Qismê [[kevçî|kevçiyê]] yê ku tiştan [[hilgirtin|hildigire]], [[zidd|ziddê]] qismê [[destik|destikê]] (ku ti tiştî [[girtin|nagire]] nava xwe). ## Qismeke [[qelûn]]ê an [[nargîle]]yê ku ji bo [[kêşan|kêşanê]] bi [[marijuana]] ([[çeşîd|çeşîdeke]] [[esrar|esrarê]]) hatiye tijîkirin. ## {{f|en|tîpografî}} [[parçe|Parçeyek]] [[gilover|gilover]] a [[glîf|glîfê]] ku [[cih|cihê]] [[vala|vala]] [[peçandin|dipêçe]], wekî di [[herf|herfên]] "d" û "o"yê de. # {{f|en|landforms}} [[krater|Krater]]eke gilover (an wekî wê) yê di [[zemîn|zemînê]] de. #: {{hevmaneyên peyvê|en|crater|hollow}} # {{f|en|spor|tiyatro}} [[stadyum|Stadyûm]] an [[amfîtiyatro|amfîtiyatroyeke]] bi şiklê [[elîpsê|elîpsê]] yê ku kaseyê tîna bîra mirov. # {{f|en|futbola amerîkî}} [[pêşbazî|Pêşbaziyek]] [[futbol|futbolê]] yê [[piştî]] [[sezon|sezonê]] (wekî nimûne ''Rose Bowl'', ''Super Bowl'') ==== Jê ==== {{kol3|en |begging bowl |[[bowl cut]] |bowl game |bowl lute |coffee bowl |dustbowl |fruit bowl |mixing bowl |pudding bowl |salad bowl |singing bowl |soup bowl |toilet bowl |bowl barrow|bowl chair|bowl hat|bowl of cherries|bowl pack|bowl wax|Buddha bowl|clean bowl|doggy dinner bowl look|finger bowl|fish bowl|food bowl|goldfish bowl|iron rice bowl|lawn-bowl|mazer bowl|poké bowl|pudding-bowl|punch bowl|punch bowl waterfall|quiz bowl|rice bowl|scorpion bowl|shampoo bowl|shit bowl|shit-bowl|slop bowl|slop-bowl|spaghetti bowl|spoon bowl|spoon-bowl|sugar bowl|swill-bowl|take the browns to the super bowl|tea bowl|track and bowl system|volcano bowl|work bowl|Zuni bowl}} ==== Etîmolojî ==== Ji {{mîr|enm|en|bolle}}, ji {{mîr|ang|en|bolla}}, {{qal|ang|bolle||kase}}, ji {{mîr|gmw-pro|en|*bollā}}, ji {{mîr|gem-pro|en|*bullǭ||top, amana gilover}}. Bide ber {{hv|frr|bol}}, {{hv|gml|bolle}}, {{qal|gml|bole||objeya gilover}}, {{hv|nl|bol||top, kure}}, {{hv|de|Bolle}}, {{hv|da|bolle}}, {{hv|is|bolli}}. {{cotreh|en|boule|pulla}}. ==== Dûnde ==== * {{dû|bi|bol}} * {{dû|eo|bovlo|deyn=1}} * {{dû|fr|bol|deyn=1}} * {{dû|de|Bowle|Bowl|deyn=1}} ** {{dû|hu|bólé|deyn=1}} * {{dû|io|bolo|deyn=1}} * {{dû|ga|bolla|deyn=1}} * {{dû|ja|ボール|tr=bōru|deyn=1}} * {{dû|lv|bole|deyn=1}} {{mj|muhtemelen ji almanî}} * {{dû|ko|볼|deyn=1}} * {{dû|gd|bòl|deyn=1}} * {{dû|sv|bål|deyn=1}} === Navdêr === {{en-navdêr}} # [[gog|Gog]]a ku di lîstika ''lawn bowls''-ê de ji aliyê lîstikvanan ve tê gindirandin. # {{f|en|di pirjimariyê de|lê bi lêkereke yekjimar tê bikaranîn}} Lîstikek e. #: {{hevmaneyên peyvê|en|lawn bowls|lawn bowling}} ==== Jê ==== {{kol2|en|bowl-off|bowl-out}} ==== Etîmolojî ==== Ji {{mîr|enm|en|bowle}}, {{qal|enm|boule}}, ji {{ji|fro|en|boule||ball}}, ji {{ji|la|en|bulla||bubble, stud, round object}}. {{cotreh|en|poll}}. === Lêker === {{en-lêker}} # {{f|en|gh}} Bi şikleke rast topê [[gindirandin]] an [[avêtin]], di lîstika krîketê de an di lîstik û sporên mîna wê de. # {{f|en|ngh}} Ji bo avêtina topê (di krîketê de û di lîstik û sporên wekî wê de). # {{f|en|ngh}} Lîstina bowlîngê an lîstikeke wek wê. # Bi rehetî û hêsanî bar kirin an gindirandin, wekî ku li ser tekerekê be. #: {{mk|en|We were '''bowled''' rapidly along the road.|Em bi lez û bez li ser rê gindirîbûn.}} # Li tişekî dan ku digere an digindire. ==== Jê ==== {{kol3|en |bowl a googly| |bowl along |bowler |bowling |bowling alley |bowling ball |bowl out |bowl over }} ==== Dûnde ==== * {{dû|da|bowle|deyn=1}} * {{dû|nl|bowlen|deyn=1}} * {{dû|de|bowlen|deyn=1}} ==== Anagram ==== * {{anagram|en|a=blow|Blow|b'low|blow}} pj6qg4f64f3m7sxf3joicku7a8y6kcn Şablon:bêçavkanî 10 448836 6291615 6117825 2026-07-26T22:53:12Z Wikihez 7344 6291615 wikitext text/x-wiki {{hişyarî | rengê hundir = var(--wikt-palette-white, white) | rengê sîndorî = var(--wikt-palette-red-8, #C00) | rengê sîndorê çep = var(--wikt-palette-red-8, #C00) | wêne = Question_book-new.svg | nivîs = {{reng2|var(--wikt-palette-red-8, #C00)|'''Çavkaniya vê madeyê tine ye û ji ber wê peyitandina malûmatan ne mimkin e.'''}} | nivîsa jêr = <small>Ji bo başkirina vê madeyê ji kerema xwe re çavkaniyan pê ve bike. Herwiha binêre gotûbêja li [[Wîkîferheng:Daxwazên peyitandinê#{{FULLPAGENAME}}|Wîkîferheng:Daxwazên peyitandinê]].<sup class="plainlinks" style="font-weight:normal" title="Li Daxwazên peyitandinê zêde bike"> (<!-- -->[{{fullurle:Wîkîferheng:Daxwazên peyitandinê|action=edit&section=new&preload=Şablon:p-wate/preload&preloadtitle=%5B%5B{{urlencode:{{FULLPAGENAME}}}}}}%5D%5D +]<!-- -->)</sup></small><br/>{{çavkanî bigere}} }}{{#tag:indicator|[[Wêne:Question_book-new.svg|25px|Gotarên bêçavkanî|link={{#if:{{{ziman|}}}|{{PAGENAME}}#{{ucfirst:{{nav ziman|{{{ziman|}}}}}}}|Kategorî:Madeyên bêçavkanî}}]]|name=Bêçavkanî}}<includeonly>[[Kategorî:Madeyên bêçavkanî]]</includeonly><noinclude> {{belgekirin}} </noinclude> j6xviz6qyfpc2t2y4e3l0u8kupr80q3 Şablon:hişyarî 10 517656 6291614 6235346 2026-07-26T22:52:48Z Wikihez 7344 6291614 wikitext text/x-wiki {| align="{{{align|center}}}" width="80%" id="{{{id|}}}" style="padding:0em; margin-top:0.5em; margin-bottom:0.5em; border-width:1px 1px 1px 5px; border-style: solid; border-color: {{{rengê sîndorî|var(--wikt-palette-lightergrey, #EEE9E1)}}} {{{rengê sîndorê rast|{{{rengê sîndorî|var(--wikt-palette-lightergrey, #EEE9E1)}}}}}} {{{rengê sîndorî|var(--wikt-palette-lightergrey, #EEE9E1)}}} {{{rengê sîndorê çep|var(--wikt-palette-grey, grey)}}}; background:{{{rengê hundir|transparent}}}; color: var(--wikt-palette-black, #202122); line-height:1.4em;{{{style|}}}" |- {{#if:{{{wêne|}}} | {{!}} style="padding:0em 0.3em 0em 0.8em" {{!}} [[Wêne:{{{wêne}}}|{{{wênefirehî|45px}}}|link={{{link|}}}]] }} |style="padding-right:0.5em; padding-left:0.5em" | {{{nivîs}}}{{#if:{{{nivîsa jêr|}}}|<br />{{{nivîsa jêr|}}}|}} |}{{clr}}<noinclude>{{belgekirin}}</noinclude> ndmusqzu6xblpbhhkcq8q9mdxez7l3h Modul:dara kategoriyan 828 876148 6291616 6114179 2026-07-26T22:56:26Z Wikihez 7344 6291616 Scribunto text/plain local export = {} local m_utilities = require('Modul:utilities') local inFundamental = mw.loadData("Modul:dara kategoriyan/dane") local show_error, check_name, link_box, show_catfix, show_categories, show_editlink, show_pagelist, show_breadcrumbs, show_description, show_TOC local function capitalize(text) return mw.getContentLanguage():ucfirst(text) end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local template = frame.args["template"] if not template or template == "" then error("Parametreya \"template\" nehatiye dayîn.") end if mw.title.getCurrentTitle().nsText == "Şablon" then local text = {} table.insert(text, "[[Kategorî:Şablonên kategorîkirinê|*]]Ev şablon tenê di rûpelên Kategorî: de tê bikaranîn.") table.insert(text, " Ji bo agahiyên zêdetir binêre [[Modul:dara kategoriyan]] û binmodulên wê [[Modul:dara kategoriyan/") table.insert(text, template .. "]].") if frame.args["useautocat"] then table.insert(text, " Dewsa bikaranîna vê şablonê şablona ") table.insert(text, require("Modul:template link").format_link({"katbike"})) table.insert(text, " (bêyî nivîsîna tiştekî din), bi kar bîne.") end return table.concat(text) elseif mw.title.getCurrentTitle().nsText ~= "Kategorî" then error("Ev şablon tenê di rûpelên bi Kategorî: dest pê dikin de tê bikaranîn.") end local submodule = require("Modul:dara kategoriyan/" .. template) -- Get all the parameters and the label data local current if submodule.new_main then current = submodule.new_main(frame) else local info = {} for key, val in pairs(frame.args) do if val ~= "" and key ~= "useautocat" then info[key] = val end end info.template = nil current = submodule.new(info, true) end local functions = { "getBreadcrumbName", "getDataModule", "canBeEmpty", "getDescription", "getParents", "getUmbrella", } if current then for i, functionName in pairs(functions) do if type(current[functionName]) ~= "function" then require("Modul:debug").track{ "dara kategoriyan/kêmasiya fonksîyonê", "dara kategoriyan/kêmasiya fonksîyonê/" .. functionName } end end end local boxes = {} local display = {} local categories = {} if template == "topic cat" then table.insert(categories, "[[Kategorî:topic cat]]") end -- Check if the category is empty local isEmpty = mw.site.stats.pagesInCategory(mw.title.getCurrentTitle().text, "all") == 0 -- Are the parameters valid? if not current then table.insert(categories, "[[Kategorî:Kategoriyên bi etîketa xelet]]") table.insert(categories, isEmpty and "[[Kategorî:Kategoriyên vala]]" or nil) table.insert(display, show_error( "Etîketa di şablona " .. require("Modul:template link").format_link{template} .. " de naxebite. Dibe ku xelet be yan hîn nehatibe çêkirin. " .. "Ji bo lêzêdekirina etîketa nû ji kerema xwe binêre belgekirina şablonê.")) -- Exit here, as all code beyond here relies on current not being nil return table.concat(categories, "") .. table.concat(display, "\n\n") end -- Does the category have the correct name? if mw.title.getCurrentTitle().text ~= current:getCategoryName() then table.insert(categories, "[[Kategorî:Kategoriyên bi navê xelet]]") table.insert(display, show_error( "Li gorî parametreyên şablona " .. require("Modul:template link").format_link{template} .. ", navê vê kategoriyê divê '''[[:Kategorî:" .. current:getCategoryName() .. "]]''' be.")) end -- Add cleanup category for empty categories local canBeEmpty = current:canBeEmpty() if isEmpty and not canBeEmpty then table.insert(categories, "[[Kategorî:Kategoriyên vala]]") end if current:isHidden() then table.insert(categories, "__HIDDENCAT__") end if canBeEmpty then table.insert(categories, " __EXPECTUNUSEDCATEGORY__") end table.insert(boxes, show_editlink(current)) -- Generate the displayed information table.insert(display, show_breadcrumbs(current)) table.insert(display, show_description(current)) table.insert(display, show_TOC(current)) table.insert(display, show_catfix(current)) show_categories(current, categories) return table.concat(boxes, "\n") .. "\n" .. table.concat(display, "\n\n") .. table.concat(categories, "") end function show_error(text) return mw.getCurrentFrame():expandTemplate{title = "maintenance box", args = { "red", image = "[[File:Ambox warning pn.svg|50px]]", title = "Xeletî di vê kategoriyê de hene.", text = text, }} end -- Check the name of the current page, and return an error if it's not right. function check_name(current, template, info) local errortext = nil local category = nil if not current then errortext = "Etîketa \"" .. (info.label or "") .. "\" di şablona " .. require("Modul:template link").format_link{template} .. " de naxebite. " .. "Dibe ku xelet be yan hîn nehatibe çêkirin. Ji bo lêzêdekirina etîketa nû ji kerema xwe binêre belgekirina şablonê." category = "[[Kategorî:Kategoriyên bi etîketa xelet]]" else end if errortext then return (category or "") .. show_error(errortext) else return nil end end function show_catfix(current) local skip_etiket = { ["werger"] = true, ["daxwazên peyvan"] = true, ["daxwazên skrîpta peyvê"] = true, ["transliterasyona nepêwîst"] = true, } local etiket = current:getInfo().label if skip_etiket[etiket] then return nil end if current._lang and not (current._info and current._info.no_catfix) then -- Use pcall to safely execute the potentially problematic code local success, result = pcall(function() local script = nil if current._info and current._info.sc then script = require("Modul:scripts").getByCode(current._info.sc) end return m_utilities.catfix(current._lang, script) end) -- If successful, return the result, otherwise return nil if success then return result else return nil -- Silently ignore any errors end else return nil end end -- Show the parent categories that the current category should be placed in. function show_categories(current, categories) local parents = current:getParents() if not parents then return end for _, parent in ipairs(parents) do if type(parent.name) == "string" then if not (current._lang and current:getCategoryName() == capitalize(current._lang:getCategoryName())) and not (current._sc and current:getCategoryName():find(capitalize(current._sc:getCategoryName()), nil, true)) and current:getInfo().code then require("Modul:debug").track("category tree/string") end table.insert(categories, "[[" .. parent.name .. "|" .. parent.sort .. "]]") else table.insert(categories, "[[Kategorî:" .. parent.name:getCategoryName() .. "|" .. parent.sort .. "]]") end end -- Also put the category in its corresponding "umbrella" or "by language" category. local umbrella = current:getUmbrella() local naveModule = current:getDataModule() if umbrella then local sort if current._lang then sort = current._lang:getCanonicalName() elseif naveModule == "Modul:dara kategoriyan/malbatkatbike" then sort = mw.ustring.gsub(current:getCategoryName(), "Zimanên ", "") else sort = current:getCategoryName() end if type(umbrella) == "string" then table.insert(categories, "[[" .. umbrella .. "|" .. sort .. "]]") else table.insert(categories, "[[Kategorî:" .. umbrella:getCategoryName() .. "|" .. sort .. "]]") end end end function link_box(content) return "<div class=\"tablo-flip noprint plainlinks nomobile\" style=\"margin: 0 0 .5em 1em; background: var(--wikt-palette-paleblue, #f9f9f9); border: 1px var(--wikt-palette-grey, #aaaaaa) solid; margin-top: -1px; padding: 5px; font-weight: bold; color: var(--wikt-palette-black, #202122);\">" .. content .. "</div>" end function show_editlink(current) return link_box( "[" .. tostring(mw.uri.fullUrl(current:getDataModule(), "action=edit")) .. " Daneya kategoriyê biguhêre]") end -- Show navigational "breadcrumbs" at the top of the page. function show_breadcrumbs(current) local steps = {} -- Start at the current label and move our way up the "chain" from child to parent, until we can't go further. while current do local category = nil local display_name = nil if type(current) == "string" then category = current display_name = current:gsub("^Kategorî:", "") else category = "Kategorî:" .. current:getCategoryName() display_name = current:getBreadcrumbName() end display_name = capitalize(display_name) table.insert(steps, 1, "[[:" .. category .. "|" .. display_name .. "]]") -- Move up the "chain" by one level. if type(current) == "string" then current = nil else current = current:getParents() end if current then current = current[1].name elseif inFundamental[category] then ---fundamental li ser Wîkîferhengê !Serkategorî ye current = "Kategorî:!Serkategorî" end end steps = table.concat(steps, " » ") return "<small>" .. steps .. "</small>" end -- Show a short description text for the category. function show_description(current) return (current:getDescription() or "") end -- Show a table of contents with links to each letter in the language's script. function show_TOC(current) local code = current:getInfo().code if code and not require("Modul:languages").getByCode(code) then return nil end if not code then code = "ku" end local etiket = current:getInfo().label if etiket == "werger" or etiket == "daxwazên peyvan" then code = "ku" end -- If category can be empty, then it only contains subcategories. local hasPages = not current:canBeEmpty() local titleText = mw.title.getCurrentTitle().text local inCategory if hasPages then inCategory = mw.site.stats.pagesInCategory(titleText, "pages") else inCategory = mw.site.stats.pagesInCategory(titleText, "subcats") end -- No need for a TOC if all pages or subcategories can fit on one page. if inCategory > 200 then -- This category is very large, see if there is an "extended" version of the TOC. if inCategory > 1000 then local TOC_template_extended = mw.title.new("Şablon:" .. code .. "-KategorîTOC dirêj") local TOC_template = mw.title.new("Şablon:".. code .. "-KategorîTOC" ) if TOC_template_extended.exists then return mw.getCurrentFrame():expandTemplate{title = TOC_template_extended.text, args = {}} elseif TOC_template.exists then return mw.getCurrentFrame():expandTemplate{title = TOC_template.text, args = {}} else return "[[Kategorî:Naverok hewce ye|" .. code .. "]]" end end local TOC_template = mw.title.new("Şablon:".. code .. "-KategorîTOC" ) if TOC_template.exists then return mw.getCurrentFrame():expandTemplate{title = TOC_template.text, args = {}} else return "[[Kategorî:Naverok hewce ye|" .. code .. "]]" end end return nil end function export.test(frame) local template = frame.args[1] local submodule = require("Modul:dara kategoriyan/" .. template) if submodule.new_main then current = submodule.new_main(frame) else local info = {} for key, val in pairs(frame.args) do info[key] = val; if info[key] == "" then info[key] = nil end end info.template = nil current = submodule.new(info, true) end end return export 599s52cgjdlz7nsuvybcko5q902103c Modul:dara kategoriyan/zimankatbike 828 876149 6291617 6113772 2026-07-26T23:01:58Z Wikihez 7344 6291617 Scribunto text/plain local export = {} local new_title = mw.title.new local ucfirst = require("Module:string utilities").ucfirst local split = require("Module:string utilities").split local m_languages = require("Module:languages") local m_sc_getByCode = require("Module:scripts").getByCode local m_table = require("Module:table") local parse_utilities_module = "Module:parse utilities" local concat = table.concat local insert = table.insert local reverse_ipairs = m_table.reverseIpairs local serial_comma_join = m_table.serialCommaJoin local size = m_table.size local sorted_pairs = m_table.sortedPairs local to_json = require("Module:JSON").toJSON local Hang = m_sc_getByCode("Hang") local Hani = m_sc_getByCode("Hani") local Hira = m_sc_getByCode("Hira") local Hrkt = m_sc_getByCode("Hrkt") local Kana = m_sc_getByCode("Kana") local function makeCategoryLink(object) return "[[:Kategorî:" .. object:getCategoryName() .. "|" .. object:getCanonicalName() .. "]]" end local function linkbox(lang, setwiki, setwikt, setsister, entryname) local wiktionarylinks = "''Tine.''" local canonicalName = lang:getCanonicalName() local wikimediaLanguages = lang:getWikimediaLanguages() local categoryName = lang:getCategoryName() local categoryName = mw.ustring.lower( categoryName ) local wikipediaArticle = setwiki or lang:getWikipediaArticle() if setwikt then require "Modul:debug".track "langcatboiler/setwikt" if setwikt == "-" then require "Modul:debug".track "langcatboiler/setwikt/hyphen" end end if setwikt ~= "-" and wikimediaLanguages and wikimediaLanguages[1] then wiktionarylinks = {} for _, wikimedialang in ipairs(wikimediaLanguages) do table.insert(wiktionarylinks, (wikimedialang:getCanonicalName() ~= canonicalName and "(''" .. wikimedialang:getCanonicalName() .. "'') " or "") .. "'''[[:" .. wikimedialang:getCode() .. ":|" .. wikimedialang:getCode() .. ".wiktionary.org]]'''") end wiktionarylinks = table.concat(wiktionarylinks, "<br/>") end local plural = wikimediaLanguages[2] and "s" or "" return table.concat{ [=[<div class="tablo-flip" style="border: solid var(--wikt-palette-grey, #aaa) 1px; margin: 1 1 1 1; background: var(--wikt-palette-paleblue, #f9f9f9); width: 270px; padding: 5px; margin: 5px;"> <div style="text-align: center; margin-bottom: 10px; margin-top: 5px">'''Zimanê ]=], categoryName, [=['''</div> {| style="font-size: 90%; background: var(--wikt-palette-paleblue, #f9f9f9);" |- | style="vertical-align: middle; height: 35px; width: 35px;" | [[File:Wiktionary-logo-v2.svg|35px|none|Wiktionary]] || ''Çapa ]=], categoryName, [=[ ya Wîkîferhengê'' |- | colspan="2" style="padding-left: 10px; border-bottom: 1px solid var(--wikt-palette-lightgrey, lightgray);" | ]=], wiktionarylinks, [=[ |- | style="vertical-align: middle; height: 35px" | [[File:Wikipedia-logo.png|35px|none|Wikipedia]] || ''Meqaleya Wîkîpediyê ya li ser ]=], categoryName, [=['' |- | colspan="2" style="padding-left: 10px; border-bottom: 1px solid var(--wikt-palette-lightgrey, lightgray);" | ]=], (setwiki == "-" and "''Tine.''" or "'''[[w:" .. wikipediaArticle .. "|" .. wikipediaArticle .. "]]'''"), [=[ |- | style="vertical-align: middle; height: 35px" | [[File:Open book nae 02.svg|35px|none|Made]] || Madeya Wîkîferhengê ya li ser ]=], categoryName,[=[ |- | colspan="2" style="padding-left: 10px;" | ''']=], require("Modul:links").full_link({lang = require("Modul:languages").getByCode("ku"), term = categoryName}), [=[''' |} </div>]=] } end local function edit_link(title, text) return '<span class="plainlinks">[' .. tostring(mw.uri.fullUrl(title, { action = "edit" })) .. ' ' .. text .. ']</span>' end -- Should perhaps use wiki syntax. local function infobox(lang) local ret = {} insert(ret, '<table class="wikitable language-category-info"') local raw_data = lang:getData("extra") if raw_data then local replacements = { [1] = "canonical-name", [2] = "wikidata-item", [3] = "family", [4] = "scripts", } local function replacer(letter1, letter2) return letter1:lower() .. "-" .. letter2:lower() end -- For each key in the language data modules, returns a descriptive -- kebab-case version (containing ASCII lowercase words separated -- by hyphens). local function kebab_case(key) key = replacements[key] or key key = key:gsub("(%l)(%u)", replacer):gsub("(%l)_(%l)", replacer) return key end local compress = {compress = true} local function html_attribute_encode(str) str = to_json(str, compress) :gsub('"', "&quot;") -- & in attributes is automatically escaped. -- :gsub("&", "&amp;") :gsub("<", "&lt;") :gsub(">", "&gt;") return str end insert(ret, ' data-code="' .. lang:getCode() .. '"') for k, v in sorted_pairs(raw_data) do insert(ret, " data-" .. kebab_case(k) .. '="' .. html_attribute_encode(v) .. '"') end end insert(ret, '>\n') insert(ret, '<tr class="language-category-data">\n<th colspan="2">' .. edit_link(lang:getDataModuleName(), "Datayên vî zimanî biguhêre") .. "</th>\n</tr>\n") insert(ret, "<tr>\n<th>Navê zimanî</th><td>" .. ucfirst(lang:getCanonicalName()) .. "</td>\n</tr>\n") local otherNames = lang:getOtherNames() if otherNames then local names = {} for _, name in ipairs(otherNames) do insert(names, "<li>" .. ucfirst(name) .. "</li>") end if #names > 0 then insert(ret, "<tr>\n<th>Navên din</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end local aliases = lang:getAliases() if aliases then local names = {} for _, name in ipairs(aliases) do insert(names, "<li>" ..ucfirst(name) .. "</li>") end if #names > 0 then insert(ret, "<tr>\n<th>Bernav</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end local varieties = lang:getVarieties() if varieties then local names = {} for _, name in ipairs(varieties) do if type(name) == "string" then insert(names, "<li>" .. ucfirst(name) .. "</li>") else assert(type(name) == "table") local first_var local subvars = {} for i, var in ipairs(name) do if i == 1 then first_var = var else insert(subvars, "<li>" .. ucfirst(var) .. "</li>") end end if #subvars > 0 then insert(names, "<li><dl><dt>" .. first_var .. "</dt>\n<dd><ul>" .. concat(subvars, "\n") .. "</ul></dd></dl></li>") elseif first_var then insert(names, "<li>" .. first_var .. "</li>") end end end if #names > 0 then insert(ret, "<tr>\n<th>Varyant</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end insert(ret, "<tr>\n<th>[[WF:LZ|Koda zimanî]]</th><td><code>" .. lang:getCode() .. "</code></td>\n</tr>\n") insert(ret, "<tr>\n<th>[[WF:Lîsteya malbatan|Malbata zimanî]]</th>\n") local fam = lang:getFamily() local famCode = fam and fam:getCode() if not fam then insert(ret, "<td>nehatiye sinifandin</td>") elseif famCode == "qfa-iso" then insert(ret, "<td>[[:Kategorî:Zimanên îzole|Zimanekî îzole]]</td>") elseif famCode == "qfa-mix" then insert(ret, "<td>[[:Kategorî:Zimanên têkilkirî|Zimanekî têkilkirî]]</td>") elseif famCode == "sgn" then insert(ret, "<td>[[:Kategorî:Zimanên destan|Zimanê destan]]</td>") elseif famCode == "crp" then insert(ret, "<td>[[:Kategorî:Zimanên kreyolî yan pîdgînî|Kreyolî yan pîdgînî]]</td>") elseif famCode == "art" then insert(ret, "<td>[[:Kategorî:Zimanên çêkirî|Zimanekî çêkirî]]</td>") else insert(ret, "<td>" .. ucfirst(fam:makeCategoryLink()) .. "</td>") end insert(ret, "\n</tr>\n<tr>\n<th>Zimanên pêşî</th>\n<td>") local ancestors = lang:getAncestors() if ancestors[2] then local ancestorList = {} for i, anc in ipairs(ancestors) do ancestorList[i] = "<li>" .. anc:makeCategoryLink() .. "</li>" end insert(ret, "<ul>\n" .. concat(ancestorList, "\n") .. "</ul>") else local ancestorChain = lang:getAncestorChainOld() if ancestorChain[1] then local chain = {} for _, anc in reverse_ipairs(ancestorChain) do insert(chain, "<li>" .. anc:makeCategoryLink() .. "</li>") end insert(ret, "<ul>\n" .. concat(chain, "\n<ul>\n") .. ("</ul>"):rep(#chain)) else insert(ret, "unknown") end end insert(ret, "</td>\n</tr>\n") local scripts = lang:getScripts() if scripts[1] then local script_text = {} local function makeScriptLine(sc) local code = sc:getCode() local url = tostring(mw.uri.fullUrl('Special:Search', { search = 'contentmodel:css insource:"' .. code .. '" insource:/\\.' .. code .. '/', ns8 = '1' })) return sc:getCanonicalName() .. ' (<span class="plainlinks" title="Search for stylesheets referencing this script">[' .. url .. ' <code>' .. code .. '</code>]</span>)' end local function add_Hrkt(text) insert(text, "<li>" .. makeScriptLine(Hrkt)) insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hira) .. "</li>") insert(text, "<li>" .. makeScriptLine(Kana) .. "</li>") insert(text, "</ul>") insert(text, "</li>") end for _, sc in ipairs(scripts) do local text = {} local code = sc:getCode() if code == "Hrkt" then add_Hrkt(text) else insert(text, "<li>" .. makeScriptLine(sc)) if code == "Jpan" then insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>") add_Hrkt(text) insert(text, "</ul>") elseif code == "Kore" then insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hang) .. "</li>") insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>") insert(text, "</ul>") end insert(text, "</li>") end insert(script_text, concat(text, "\n")) end insert(ret, "<tr>\n<th>[[Modul:scripts/data|Skrîpt]]</th>\n<td><ul>\n" .. concat(script_text, "\n") .. "</ul></td>\n</tr>\n") else insert(ret, "<tr>\n<th>[[Modul:scripts/data|Skrîpt]]</th>\n<td>nehatiye nivîsin</td>\n</tr>\n") end local function add_module_info(raw_data, heading) if raw_data then local scripts = lang:getScriptCodes() local module_info, add = {}, false if type(raw_data) == "string" then insert(module_info, ("[[Module:%s]]"):format(raw_data)) add = true else local raw_data_type = type(raw_data) if raw_data_type == "table" and size(scripts) == 1 and type(raw_data[scripts[1]]) == "string" then insert(module_info, ("[[Module:%s]]"):format(raw_data[scripts[1]])) add = true elseif raw_data_type == "table" then insert(module_info, "<ul>") for script, data in sorted_pairs(raw_data) do if type(data) == "string" and m_sc_getByCode(script) then insert(module_info, ("<li><code>%s</code>: [[Module:%s]]</li>"):format(script, data)) end end insert(module_info, "</ul>") add = size(module_info) > 2 end end if add then insert(ret, [=[ <tr> <th>]=] .. heading .. [=[</th> <td>]=] .. concat(module_info) .. [=[</td> </tr> ]=]) end end end add_module_info(raw_data.generate_forms, "Form-generating<br>module") add_module_info(raw_data.translit, "Modula<br>translîterasyonê") add_module_info(raw_data.display_text, "Display text<br>module") add_module_info(raw_data.entry_name, "Entry name<br>module") add_module_info(raw_data.sort_key, "Modula<br>rêzkirinê") local wikidataItem = lang:getWikidataItem() if lang:getWikidataItem() and mw.wikibase then local URL = mw.wikibase.getEntityUrl(wikidataItem) local link if URL then link = '[' .. URL .. ' ' .. wikidataItem .. ']' else link = '<span class="error">Invalid Wikidata item: <code>' .. wikidataItem .. '</code></span>' end insert(ret, "<tr><th>Wîkîdata</th><td>" .. link .. "</td></tr>") end insert(ret, "</table>") return concat(ret) end local function NavFrame(content, title) return '<div class="NavFrame"><div class="NavHead">' .. (title or '{{{title}}}') .. '</div>' .. '<div class="NavContent" style="text-align: left;">' .. content .. '</div></div>' end function export.country_categories(frame) end -- Category object local Category = {} Category.__index = Category function Category.new(info) for key, val in pairs(info) do if not (key == "code" or key == "entryname" or key == "setsister" or key == "setwiki" or key == "setwikt") then error("The parameter \"" .. key .. "\" was not recognized.") end end local self = setmetatable({}, Category) self._info = info if not self._info.code then error("Koda zimanî nehatiye nivîsîn.") else self._lang = require("Modul:languages").getByCode(self._info.code) or error("Koda zimanî \"" .. self._info.code .. "\" ne guncav e.") end return self end export.new = Category.new function Category:getInfo() return self._info end function Category:getBreadcrumbName() return self._lang:getCanonicalName() end function Category:getDataModule() return "Modul:dara kategoriyan/zimankatbike" end function Category:canBeEmpty() return false end function Category:isHidden() return false end function Category:getCategoryName() return mw.getContentLanguage():ucfirst(self._lang:getCategoryName()) end function Category:getDescription() if self._lang:getCode() == "und" then return "Kategoriya sereke ya '''" .. self._lang:getCategoryName() .. "''', represented in Wiktionary by the [[Wiktionary:Languages|code]] '''" .. self._lang:getCode() .. "'''. " .. "This language contains terms in historical writing, whose meaning has not yet been determined by scholars." end local canonicalName = self._lang:getCanonicalName() local ret = linkbox(self._lang, self._info.setwiki, self._info.setwikt, self._info.setsister, self._info.entryname) ret = ret .. "Ev ji bo zimanê '''" .. mw.ustring.lower(self._lang:getCategoryName()) .. "''' kategoriya sereke ye.\n\nDanasîna zimanî:\n\n" .. infobox(self._lang) if self._lang:hasType("reconstructed") then ret = ret .. "\n\n" .. mw.getContentLanguage():ucfirst(canonicalName) .. " zimanekî ji nû ve sazkirî ye. Lewra peyvên di vê rûpelê de yekser nehatine peyitandin." .. " Lê belê li gorî rêbaza berawirdkirinê piştraskirî ne û tên bawerkirin ku di wextekî de hebûn.\n\n" .. "Peyvên bi zimanê " .. canonicalName .. " nabe ku cihê xwe valahiya nav ya sereke de bigrin, van peyvan divê bi [[Wîkîferheng:Jinûvesazî|'''Jinûvesazî:''']] dest pê bikin." -- elseif self._lang:hasType("appendix-constructed") then -- ret = ret .. "\n\n" .. -- ucfirst(canonicalName) .. " zimanekî çêkirî ye. Loma " .. -- "divê madeyên bi " .. canonicalName .. -- " di valahiya nav a Pêvek: de werin qeydkirin. " .. -- "Ji bo hemû madeyan binêre [[Pêvek:" .. ucfirst(canonicalName) .. "]]." end ret = ret .. " __EXPECTUNUSEDCATEGORY__" -- it's OK if this category is empty return ret end function Category:getParents() local canonicalName = self._lang:getCanonicalName() local ret = {{name = "Kategorî:Hemû ziman", sort = canonicalName}} local fam = self._lang:getFamily() local famCode = fam and fam:getCode() if not fam then table.insert(ret, {name = "Kategorî:Zimanên ku nehatine sinifandin", sort = canonicalName}) elseif famCode == "qfa-iso" then table.insert(ret, {name = "Kategorî:Zimanên îzole", sort = canonicalName}) elseif famCode == "qfa-mix" then table.insert(ret, {name = "Kategorî:Zimanên têkilkirî", sort = canonicalName}) elseif famCode == "sgn" then table.insert(ret, {name = "Kategorî:Zimanên destan", sort = canonicalName}) elseif famCode == "crp" then table.insert(ret, {name = "Kategorî:Zimanên kreyolî yan pîdgînî", sort = canonicalName}) for _, anc in ipairs(self._lang:getAncestors()) do table.insert(ret, {name = "Kategorî:Zimanên kreyolî yan pîdgînî li ser bingeha " .. anc:getCanonicalName(), sort = canonicalName}) end elseif famCode == "art" then table.insert(ret, {name = "Kategorî:Zimanên çêkirî", sort = canonicalName}) -- if self._lang:hasType("appendix-constructed") then -- table.insert(ret, {name = "Kategorî:Zimanên çêkirî di pêvekan de", sort = canonicalName}) -- else -- table.insert(ret, {name = "Kategorî:Zimanên çêkirî", sort = canonicalName}) -- end for _, anc in ipairs(self._lang:getAncestors()) do table.insert(ret, {name = "Kategorî:Li ser bingeha " .. anc:getCanonicalName() .. " zimanên çêkirî", sort = canonicalName}) end else table.insert(ret, {name = "Kategorî:" .. mw.getContentLanguage():ucfirst(fam:getCategoryName()), sort = canonicalName}) if self._lang:hasType("reconstructed") then table.insert(ret, {name = "Kategorî:Zimanên jinûvesazkirî", sort = (mw.ustring.gsub(canonicalName, "^Proto%-", ""))}) end end if self._lang:hasTranslit() then table.insert(ret, {name = "Kategorî:Zimanên bi transliterasyona xweser", sort = canonicalName}) end return ret end function Category:getUmbrella() return nil end return export jv30ooj0ih8by4wsqqgma5ch20s09et 6291618 6291617 2026-07-26T23:06:49Z Wikihez 7344 6291618 Scribunto text/plain local export = {} local new_title = mw.title.new local ucfirst = require("Module:string utilities").ucfirst local split = require("Module:string utilities").split local m_languages = require("Module:languages") local m_sc_getByCode = require("Module:scripts").getByCode local m_table = require("Module:table") local parse_utilities_module = "Module:parse utilities" local concat = table.concat local insert = table.insert local reverse_ipairs = m_table.reverseIpairs local serial_comma_join = m_table.serialCommaJoin local size = m_table.size local sorted_pairs = m_table.sortedPairs local to_json = require("Module:JSON").toJSON local Hang = m_sc_getByCode("Hang") local Hani = m_sc_getByCode("Hani") local Hira = m_sc_getByCode("Hira") local Hrkt = m_sc_getByCode("Hrkt") local Kana = m_sc_getByCode("Kana") local function makeCategoryLink(object) return "[[:Kategorî:" .. object:getCategoryName() .. "|" .. object:getCanonicalName() .. "]]" end local function linkbox(lang, setwiki, setwikt, setsister, entryname) local wiktionarylinks = "''Tine.''" local canonicalName = lang:getCanonicalName() local wikimediaLanguages = lang:getWikimediaLanguages() local categoryName = lang:getCategoryName() local categoryName = mw.ustring.lower( categoryName ) local wikipediaArticle = setwiki or lang:getWikipediaArticle() if setwikt then require "Modul:debug".track "langcatboiler/setwikt" if setwikt == "-" then require "Modul:debug".track "langcatboiler/setwikt/hyphen" end end if setwikt ~= "-" and wikimediaLanguages and wikimediaLanguages[1] then wiktionarylinks = {} for _, wikimedialang in ipairs(wikimediaLanguages) do table.insert(wiktionarylinks, (wikimedialang:getCanonicalName() ~= canonicalName and "(''" .. wikimedialang:getCanonicalName() .. "'') " or "") .. "'''[[:" .. wikimedialang:getCode() .. ":|" .. wikimedialang:getCode() .. ".wiktionary.org]]'''") end wiktionarylinks = table.concat(wiktionarylinks, "<br/>") end local plural = wikimediaLanguages[2] and "s" or "" return table.concat{ [=[<div class="tablo-flip" style="border: solid var(--wikt-palette-grey, #aaa) 1px; margin: 1 1 1 1; background: var(--wikt-palette-lightergrey, #f9f9f9); width: 270px; padding: 5px; margin: 5px;"> <div style="text-align: center; margin-bottom: 10px; margin-top: 5px">'''Zimanê ]=], categoryName, [=['''</div> {| style="font-size: 90%; background: var(--wikt-palette-lightergrey, #f9f9f9);" |- | style="vertical-align: middle; height: 35px; width: 35px;" | [[File:Wiktionary-logo-v2.svg|35px|none|Wiktionary]] || ''Çapa ]=], categoryName, [=[ ya Wîkîferhengê'' |- | colspan="2" style="padding-left: 10px; border-bottom: 1px solid var(--wikt-palette-lightgrey, lightgray);" | ]=], wiktionarylinks, [=[ |- | style="vertical-align: middle; height: 35px" | [[File:Wikipedia-logo.png|35px|none|Wikipedia]] || ''Meqaleya Wîkîpediyê ya li ser ]=], categoryName, [=['' |- | colspan="2" style="padding-left: 10px; border-bottom: 1px solid var(--wikt-palette-lightgrey, lightgray);" | ]=], (setwiki == "-" and "''Tine.''" or "'''[[w:" .. wikipediaArticle .. "|" .. wikipediaArticle .. "]]'''"), [=[ |- | style="vertical-align: middle; height: 35px" | [[File:Open book nae 02.svg|35px|none|Made]] || Madeya Wîkîferhengê ya li ser ]=], categoryName,[=[ |- | colspan="2" style="padding-left: 10px;" | ''']=], require("Modul:links").full_link({lang = require("Modul:languages").getByCode("ku"), term = categoryName}), [=[''' |} </div>]=] } end local function edit_link(title, text) return '<span class="plainlinks">[' .. tostring(mw.uri.fullUrl(title, { action = "edit" })) .. ' ' .. text .. ']</span>' end -- Should perhaps use wiki syntax. local function infobox(lang) local ret = {} insert(ret, '<table class="wikitable language-category-info"') local raw_data = lang:getData("extra") if raw_data then local replacements = { [1] = "canonical-name", [2] = "wikidata-item", [3] = "family", [4] = "scripts", } local function replacer(letter1, letter2) return letter1:lower() .. "-" .. letter2:lower() end -- For each key in the language data modules, returns a descriptive -- kebab-case version (containing ASCII lowercase words separated -- by hyphens). local function kebab_case(key) key = replacements[key] or key key = key:gsub("(%l)(%u)", replacer):gsub("(%l)_(%l)", replacer) return key end local compress = {compress = true} local function html_attribute_encode(str) str = to_json(str, compress) :gsub('"', "&quot;") -- & in attributes is automatically escaped. -- :gsub("&", "&amp;") :gsub("<", "&lt;") :gsub(">", "&gt;") return str end insert(ret, ' data-code="' .. lang:getCode() .. '"') for k, v in sorted_pairs(raw_data) do insert(ret, " data-" .. kebab_case(k) .. '="' .. html_attribute_encode(v) .. '"') end end insert(ret, '>\n') insert(ret, '<tr class="language-category-data">\n<th colspan="2">' .. edit_link(lang:getDataModuleName(), "Datayên vî zimanî biguhêre") .. "</th>\n</tr>\n") insert(ret, "<tr>\n<th>Navê zimanî</th><td>" .. ucfirst(lang:getCanonicalName()) .. "</td>\n</tr>\n") local otherNames = lang:getOtherNames() if otherNames then local names = {} for _, name in ipairs(otherNames) do insert(names, "<li>" .. ucfirst(name) .. "</li>") end if #names > 0 then insert(ret, "<tr>\n<th>Navên din</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end local aliases = lang:getAliases() if aliases then local names = {} for _, name in ipairs(aliases) do insert(names, "<li>" ..ucfirst(name) .. "</li>") end if #names > 0 then insert(ret, "<tr>\n<th>Bernav</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end local varieties = lang:getVarieties() if varieties then local names = {} for _, name in ipairs(varieties) do if type(name) == "string" then insert(names, "<li>" .. ucfirst(name) .. "</li>") else assert(type(name) == "table") local first_var local subvars = {} for i, var in ipairs(name) do if i == 1 then first_var = var else insert(subvars, "<li>" .. ucfirst(var) .. "</li>") end end if #subvars > 0 then insert(names, "<li><dl><dt>" .. first_var .. "</dt>\n<dd><ul>" .. concat(subvars, "\n") .. "</ul></dd></dl></li>") elseif first_var then insert(names, "<li>" .. first_var .. "</li>") end end end if #names > 0 then insert(ret, "<tr>\n<th>Varyant</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end insert(ret, "<tr>\n<th>[[WF:LZ|Koda zimanî]]</th><td><code>" .. lang:getCode() .. "</code></td>\n</tr>\n") insert(ret, "<tr>\n<th>[[WF:Lîsteya malbatan|Malbata zimanî]]</th>\n") local fam = lang:getFamily() local famCode = fam and fam:getCode() if not fam then insert(ret, "<td>nehatiye sinifandin</td>") elseif famCode == "qfa-iso" then insert(ret, "<td>[[:Kategorî:Zimanên îzole|Zimanekî îzole]]</td>") elseif famCode == "qfa-mix" then insert(ret, "<td>[[:Kategorî:Zimanên têkilkirî|Zimanekî têkilkirî]]</td>") elseif famCode == "sgn" then insert(ret, "<td>[[:Kategorî:Zimanên destan|Zimanê destan]]</td>") elseif famCode == "crp" then insert(ret, "<td>[[:Kategorî:Zimanên kreyolî yan pîdgînî|Kreyolî yan pîdgînî]]</td>") elseif famCode == "art" then insert(ret, "<td>[[:Kategorî:Zimanên çêkirî|Zimanekî çêkirî]]</td>") else insert(ret, "<td>" .. ucfirst(fam:makeCategoryLink()) .. "</td>") end insert(ret, "\n</tr>\n<tr>\n<th>Zimanên pêşî</th>\n<td>") local ancestors = lang:getAncestors() if ancestors[2] then local ancestorList = {} for i, anc in ipairs(ancestors) do ancestorList[i] = "<li>" .. anc:makeCategoryLink() .. "</li>" end insert(ret, "<ul>\n" .. concat(ancestorList, "\n") .. "</ul>") else local ancestorChain = lang:getAncestorChainOld() if ancestorChain[1] then local chain = {} for _, anc in reverse_ipairs(ancestorChain) do insert(chain, "<li>" .. anc:makeCategoryLink() .. "</li>") end insert(ret, "<ul>\n" .. concat(chain, "\n<ul>\n") .. ("</ul>"):rep(#chain)) else insert(ret, "unknown") end end insert(ret, "</td>\n</tr>\n") local scripts = lang:getScripts() if scripts[1] then local script_text = {} local function makeScriptLine(sc) local code = sc:getCode() local url = tostring(mw.uri.fullUrl('Special:Search', { search = 'contentmodel:css insource:"' .. code .. '" insource:/\\.' .. code .. '/', ns8 = '1' })) return sc:getCanonicalName() .. ' (<span class="plainlinks" title="Search for stylesheets referencing this script">[' .. url .. ' <code>' .. code .. '</code>]</span>)' end local function add_Hrkt(text) insert(text, "<li>" .. makeScriptLine(Hrkt)) insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hira) .. "</li>") insert(text, "<li>" .. makeScriptLine(Kana) .. "</li>") insert(text, "</ul>") insert(text, "</li>") end for _, sc in ipairs(scripts) do local text = {} local code = sc:getCode() if code == "Hrkt" then add_Hrkt(text) else insert(text, "<li>" .. makeScriptLine(sc)) if code == "Jpan" then insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>") add_Hrkt(text) insert(text, "</ul>") elseif code == "Kore" then insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hang) .. "</li>") insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>") insert(text, "</ul>") end insert(text, "</li>") end insert(script_text, concat(text, "\n")) end insert(ret, "<tr>\n<th>[[Modul:scripts/data|Skrîpt]]</th>\n<td><ul>\n" .. concat(script_text, "\n") .. "</ul></td>\n</tr>\n") else insert(ret, "<tr>\n<th>[[Modul:scripts/data|Skrîpt]]</th>\n<td>nehatiye nivîsin</td>\n</tr>\n") end local function add_module_info(raw_data, heading) if raw_data then local scripts = lang:getScriptCodes() local module_info, add = {}, false if type(raw_data) == "string" then insert(module_info, ("[[Module:%s]]"):format(raw_data)) add = true else local raw_data_type = type(raw_data) if raw_data_type == "table" and size(scripts) == 1 and type(raw_data[scripts[1]]) == "string" then insert(module_info, ("[[Module:%s]]"):format(raw_data[scripts[1]])) add = true elseif raw_data_type == "table" then insert(module_info, "<ul>") for script, data in sorted_pairs(raw_data) do if type(data) == "string" and m_sc_getByCode(script) then insert(module_info, ("<li><code>%s</code>: [[Module:%s]]</li>"):format(script, data)) end end insert(module_info, "</ul>") add = size(module_info) > 2 end end if add then insert(ret, [=[ <tr> <th>]=] .. heading .. [=[</th> <td>]=] .. concat(module_info) .. [=[</td> </tr> ]=]) end end end add_module_info(raw_data.generate_forms, "Form-generating<br>module") add_module_info(raw_data.translit, "Modula<br>translîterasyonê") add_module_info(raw_data.display_text, "Display text<br>module") add_module_info(raw_data.entry_name, "Entry name<br>module") add_module_info(raw_data.sort_key, "Modula<br>rêzkirinê") local wikidataItem = lang:getWikidataItem() if lang:getWikidataItem() and mw.wikibase then local URL = mw.wikibase.getEntityUrl(wikidataItem) local link if URL then link = '[' .. URL .. ' ' .. wikidataItem .. ']' else link = '<span class="error">Invalid Wikidata item: <code>' .. wikidataItem .. '</code></span>' end insert(ret, "<tr><th>Wîkîdata</th><td>" .. link .. "</td></tr>") end insert(ret, "</table>") return concat(ret) end local function NavFrame(content, title) return '<div class="NavFrame"><div class="NavHead">' .. (title or '{{{title}}}') .. '</div>' .. '<div class="NavContent" style="text-align: left;">' .. content .. '</div></div>' end function export.country_categories(frame) end -- Category object local Category = {} Category.__index = Category function Category.new(info) for key, val in pairs(info) do if not (key == "code" or key == "entryname" or key == "setsister" or key == "setwiki" or key == "setwikt") then error("The parameter \"" .. key .. "\" was not recognized.") end end local self = setmetatable({}, Category) self._info = info if not self._info.code then error("Koda zimanî nehatiye nivîsîn.") else self._lang = require("Modul:languages").getByCode(self._info.code) or error("Koda zimanî \"" .. self._info.code .. "\" ne guncav e.") end return self end export.new = Category.new function Category:getInfo() return self._info end function Category:getBreadcrumbName() return self._lang:getCanonicalName() end function Category:getDataModule() return "Modul:dara kategoriyan/zimankatbike" end function Category:canBeEmpty() return false end function Category:isHidden() return false end function Category:getCategoryName() return mw.getContentLanguage():ucfirst(self._lang:getCategoryName()) end function Category:getDescription() if self._lang:getCode() == "und" then return "Kategoriya sereke ya '''" .. self._lang:getCategoryName() .. "''', represented in Wiktionary by the [[Wiktionary:Languages|code]] '''" .. self._lang:getCode() .. "'''. " .. "This language contains terms in historical writing, whose meaning has not yet been determined by scholars." end local canonicalName = self._lang:getCanonicalName() local ret = linkbox(self._lang, self._info.setwiki, self._info.setwikt, self._info.setsister, self._info.entryname) ret = ret .. "Ev ji bo zimanê '''" .. mw.ustring.lower(self._lang:getCategoryName()) .. "''' kategoriya sereke ye.\n\nDanasîna zimanî:\n\n" .. infobox(self._lang) if self._lang:hasType("reconstructed") then ret = ret .. "\n\n" .. mw.getContentLanguage():ucfirst(canonicalName) .. " zimanekî ji nû ve sazkirî ye. Lewra peyvên di vê rûpelê de yekser nehatine peyitandin." .. " Lê belê li gorî rêbaza berawirdkirinê piştraskirî ne û tên bawerkirin ku di wextekî de hebûn.\n\n" .. "Peyvên bi zimanê " .. canonicalName .. " nabe ku cihê xwe valahiya nav ya sereke de bigrin, van peyvan divê bi [[Wîkîferheng:Jinûvesazî|'''Jinûvesazî:''']] dest pê bikin." -- elseif self._lang:hasType("appendix-constructed") then -- ret = ret .. "\n\n" .. -- ucfirst(canonicalName) .. " zimanekî çêkirî ye. Loma " .. -- "divê madeyên bi " .. canonicalName .. -- " di valahiya nav a Pêvek: de werin qeydkirin. " .. -- "Ji bo hemû madeyan binêre [[Pêvek:" .. ucfirst(canonicalName) .. "]]." end ret = ret .. " __EXPECTUNUSEDCATEGORY__" -- it's OK if this category is empty return ret end function Category:getParents() local canonicalName = self._lang:getCanonicalName() local ret = {{name = "Kategorî:Hemû ziman", sort = canonicalName}} local fam = self._lang:getFamily() local famCode = fam and fam:getCode() if not fam then table.insert(ret, {name = "Kategorî:Zimanên ku nehatine sinifandin", sort = canonicalName}) elseif famCode == "qfa-iso" then table.insert(ret, {name = "Kategorî:Zimanên îzole", sort = canonicalName}) elseif famCode == "qfa-mix" then table.insert(ret, {name = "Kategorî:Zimanên têkilkirî", sort = canonicalName}) elseif famCode == "sgn" then table.insert(ret, {name = "Kategorî:Zimanên destan", sort = canonicalName}) elseif famCode == "crp" then table.insert(ret, {name = "Kategorî:Zimanên kreyolî yan pîdgînî", sort = canonicalName}) for _, anc in ipairs(self._lang:getAncestors()) do table.insert(ret, {name = "Kategorî:Zimanên kreyolî yan pîdgînî li ser bingeha " .. anc:getCanonicalName(), sort = canonicalName}) end elseif famCode == "art" then table.insert(ret, {name = "Kategorî:Zimanên çêkirî", sort = canonicalName}) -- if self._lang:hasType("appendix-constructed") then -- table.insert(ret, {name = "Kategorî:Zimanên çêkirî di pêvekan de", sort = canonicalName}) -- else -- table.insert(ret, {name = "Kategorî:Zimanên çêkirî", sort = canonicalName}) -- end for _, anc in ipairs(self._lang:getAncestors()) do table.insert(ret, {name = "Kategorî:Li ser bingeha " .. anc:getCanonicalName() .. " zimanên çêkirî", sort = canonicalName}) end else table.insert(ret, {name = "Kategorî:" .. mw.getContentLanguage():ucfirst(fam:getCategoryName()), sort = canonicalName}) if self._lang:hasType("reconstructed") then table.insert(ret, {name = "Kategorî:Zimanên jinûvesazkirî", sort = (mw.ustring.gsub(canonicalName, "^Proto%-", ""))}) end end if self._lang:hasTranslit() then table.insert(ret, {name = "Kategorî:Zimanên bi transliterasyona xweser", sort = canonicalName}) end return ret end function Category:getUmbrella() return nil end return export 1iahopromko3khwppr6w6wi4m3ru4ui 6291619 6291618 2026-07-26T23:17:04Z Wikihez 7344 6291619 Scribunto text/plain local export = {} local new_title = mw.title.new local ucfirst = require("Module:string utilities").ucfirst local split = require("Module:string utilities").split local m_languages = require("Module:languages") local m_sc_getByCode = require("Module:scripts").getByCode local m_table = require("Module:table") local parse_utilities_module = "Module:parse utilities" local concat = table.concat local insert = table.insert local reverse_ipairs = m_table.reverseIpairs local serial_comma_join = m_table.serialCommaJoin local size = m_table.size local sorted_pairs = m_table.sortedPairs local to_json = require("Module:JSON").toJSON local Hang = m_sc_getByCode("Hang") local Hani = m_sc_getByCode("Hani") local Hira = m_sc_getByCode("Hira") local Hrkt = m_sc_getByCode("Hrkt") local Kana = m_sc_getByCode("Kana") local function makeCategoryLink(object) return "[[:Kategorî:" .. object:getCategoryName() .. "|" .. object:getCanonicalName() .. "]]" end local function linkbox(lang, setwiki, setwikt, setsister, entryname) local wiktionarylinks = "''Tine.''" local canonicalName = lang:getCanonicalName() local wikimediaLanguages = lang:getWikimediaLanguages() local categoryName = lang:getCategoryName() local categoryName = mw.ustring.lower( categoryName ) local wikipediaArticle = setwiki or lang:getWikipediaArticle() if setwikt then require "Modul:debug".track "langcatboiler/setwikt" if setwikt == "-" then require "Modul:debug".track "langcatboiler/setwikt/hyphen" end end if setwikt ~= "-" and wikimediaLanguages and wikimediaLanguages[1] then wiktionarylinks = {} for _, wikimedialang in ipairs(wikimediaLanguages) do table.insert(wiktionarylinks, (wikimedialang:getCanonicalName() ~= canonicalName and "(''" .. wikimedialang:getCanonicalName() .. "'') " or "") .. "'''[[:" .. wikimedialang:getCode() .. ":|" .. wikimedialang:getCode() .. ".wiktionary.org]]'''") end wiktionarylinks = table.concat(wiktionarylinks, "<br/>") end local plural = wikimediaLanguages[2] and "s" or "" return table.concat{ [=[<div class="tablo-flip standard-box" style="margin: 0 0 .5em 1em; width: 270px;"> <div style="text-align: center; margin-bottom: 10px; margin-top: 5px">'''Zimanê ]=], categoryName, [=['''</div> {| style="font-size: 90%; background: var(--wikt-palette-paleblue, #f8f9fa);" |- | style="vertical-align: middle; height: 35px; width: 35px;" | [[File:Wiktionary-logo-v2.svg|35px|none|Wiktionary]] || ''Çapa ]=], categoryName, [=[ ya Wîkîferhengê'' |- | colspan="2" style="padding-left: 10px; border-bottom: 1px solid var(--wikt-palette-lightgrey, lightgray);" | ]=], wiktionarylinks, [=[ |- | style="vertical-align: middle; height: 35px" | [[File:Wikipedia-logo.png|35px|none|Wikipedia]] || ''Meqaleya Wîkîpediyê ya li ser ]=], categoryName, [=['' |- | colspan="2" style="padding-left: 10px; border-bottom: 1px solid var(--wikt-palette-lightgrey, lightgray);" | ]=], (setwiki == "-" and "''Tine.''" or "'''[[w:" .. wikipediaArticle .. "|" .. wikipediaArticle .. "]]'''"), [=[ |- | style="vertical-align: middle; height: 35px" | [[File:Open book nae 02.svg|35px|none|Made]] || Madeya Wîkîferhengê ya li ser ]=], categoryName,[=[ |- | colspan="2" style="padding-left: 10px;" | ''']=], require("Modul:links").full_link({lang = require("Modul:languages").getByCode("ku"), term = categoryName}), [=[''' |} </div>]=] } end local function edit_link(title, text) return '<span class="plainlinks">[' .. tostring(mw.uri.fullUrl(title, { action = "edit" })) .. ' ' .. text .. ']</span>' end -- Should perhaps use wiki syntax. local function infobox(lang) local ret = {} insert(ret, '<table class="wikitable language-category-info"') local raw_data = lang:getData("extra") if raw_data then local replacements = { [1] = "canonical-name", [2] = "wikidata-item", [3] = "family", [4] = "scripts", } local function replacer(letter1, letter2) return letter1:lower() .. "-" .. letter2:lower() end -- For each key in the language data modules, returns a descriptive -- kebab-case version (containing ASCII lowercase words separated -- by hyphens). local function kebab_case(key) key = replacements[key] or key key = key:gsub("(%l)(%u)", replacer):gsub("(%l)_(%l)", replacer) return key end local compress = {compress = true} local function html_attribute_encode(str) str = to_json(str, compress) :gsub('"', "&quot;") -- & in attributes is automatically escaped. -- :gsub("&", "&amp;") :gsub("<", "&lt;") :gsub(">", "&gt;") return str end insert(ret, ' data-code="' .. lang:getCode() .. '"') for k, v in sorted_pairs(raw_data) do insert(ret, " data-" .. kebab_case(k) .. '="' .. html_attribute_encode(v) .. '"') end end insert(ret, '>\n') insert(ret, '<tr class="language-category-data">\n<th colspan="2">' .. edit_link(lang:getDataModuleName(), "Datayên vî zimanî biguhêre") .. "</th>\n</tr>\n") insert(ret, "<tr>\n<th>Navê zimanî</th><td>" .. ucfirst(lang:getCanonicalName()) .. "</td>\n</tr>\n") local otherNames = lang:getOtherNames() if otherNames then local names = {} for _, name in ipairs(otherNames) do insert(names, "<li>" .. ucfirst(name) .. "</li>") end if #names > 0 then insert(ret, "<tr>\n<th>Navên din</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end local aliases = lang:getAliases() if aliases then local names = {} for _, name in ipairs(aliases) do insert(names, "<li>" ..ucfirst(name) .. "</li>") end if #names > 0 then insert(ret, "<tr>\n<th>Bernav</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end local varieties = lang:getVarieties() if varieties then local names = {} for _, name in ipairs(varieties) do if type(name) == "string" then insert(names, "<li>" .. ucfirst(name) .. "</li>") else assert(type(name) == "table") local first_var local subvars = {} for i, var in ipairs(name) do if i == 1 then first_var = var else insert(subvars, "<li>" .. ucfirst(var) .. "</li>") end end if #subvars > 0 then insert(names, "<li><dl><dt>" .. first_var .. "</dt>\n<dd><ul>" .. concat(subvars, "\n") .. "</ul></dd></dl></li>") elseif first_var then insert(names, "<li>" .. first_var .. "</li>") end end end if #names > 0 then insert(ret, "<tr>\n<th>Varyant</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end insert(ret, "<tr>\n<th>[[WF:LZ|Koda zimanî]]</th><td><code>" .. lang:getCode() .. "</code></td>\n</tr>\n") insert(ret, "<tr>\n<th>[[WF:Lîsteya malbatan|Malbata zimanî]]</th>\n") local fam = lang:getFamily() local famCode = fam and fam:getCode() if not fam then insert(ret, "<td>nehatiye sinifandin</td>") elseif famCode == "qfa-iso" then insert(ret, "<td>[[:Kategorî:Zimanên îzole|Zimanekî îzole]]</td>") elseif famCode == "qfa-mix" then insert(ret, "<td>[[:Kategorî:Zimanên têkilkirî|Zimanekî têkilkirî]]</td>") elseif famCode == "sgn" then insert(ret, "<td>[[:Kategorî:Zimanên destan|Zimanê destan]]</td>") elseif famCode == "crp" then insert(ret, "<td>[[:Kategorî:Zimanên kreyolî yan pîdgînî|Kreyolî yan pîdgînî]]</td>") elseif famCode == "art" then insert(ret, "<td>[[:Kategorî:Zimanên çêkirî|Zimanekî çêkirî]]</td>") else insert(ret, "<td>" .. ucfirst(fam:makeCategoryLink()) .. "</td>") end insert(ret, "\n</tr>\n<tr>\n<th>Zimanên pêşî</th>\n<td>") local ancestors = lang:getAncestors() if ancestors[2] then local ancestorList = {} for i, anc in ipairs(ancestors) do ancestorList[i] = "<li>" .. anc:makeCategoryLink() .. "</li>" end insert(ret, "<ul>\n" .. concat(ancestorList, "\n") .. "</ul>") else local ancestorChain = lang:getAncestorChainOld() if ancestorChain[1] then local chain = {} for _, anc in reverse_ipairs(ancestorChain) do insert(chain, "<li>" .. anc:makeCategoryLink() .. "</li>") end insert(ret, "<ul>\n" .. concat(chain, "\n<ul>\n") .. ("</ul>"):rep(#chain)) else insert(ret, "unknown") end end insert(ret, "</td>\n</tr>\n") local scripts = lang:getScripts() if scripts[1] then local script_text = {} local function makeScriptLine(sc) local code = sc:getCode() local url = tostring(mw.uri.fullUrl('Special:Search', { search = 'contentmodel:css insource:"' .. code .. '" insource:/\\.' .. code .. '/', ns8 = '1' })) return sc:getCanonicalName() .. ' (<span class="plainlinks" title="Search for stylesheets referencing this script">[' .. url .. ' <code>' .. code .. '</code>]</span>)' end local function add_Hrkt(text) insert(text, "<li>" .. makeScriptLine(Hrkt)) insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hira) .. "</li>") insert(text, "<li>" .. makeScriptLine(Kana) .. "</li>") insert(text, "</ul>") insert(text, "</li>") end for _, sc in ipairs(scripts) do local text = {} local code = sc:getCode() if code == "Hrkt" then add_Hrkt(text) else insert(text, "<li>" .. makeScriptLine(sc)) if code == "Jpan" then insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>") add_Hrkt(text) insert(text, "</ul>") elseif code == "Kore" then insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hang) .. "</li>") insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>") insert(text, "</ul>") end insert(text, "</li>") end insert(script_text, concat(text, "\n")) end insert(ret, "<tr>\n<th>[[Modul:scripts/data|Skrîpt]]</th>\n<td><ul>\n" .. concat(script_text, "\n") .. "</ul></td>\n</tr>\n") else insert(ret, "<tr>\n<th>[[Modul:scripts/data|Skrîpt]]</th>\n<td>nehatiye nivîsin</td>\n</tr>\n") end local function add_module_info(raw_data, heading) if raw_data then local scripts = lang:getScriptCodes() local module_info, add = {}, false if type(raw_data) == "string" then insert(module_info, ("[[Module:%s]]"):format(raw_data)) add = true else local raw_data_type = type(raw_data) if raw_data_type == "table" and size(scripts) == 1 and type(raw_data[scripts[1]]) == "string" then insert(module_info, ("[[Module:%s]]"):format(raw_data[scripts[1]])) add = true elseif raw_data_type == "table" then insert(module_info, "<ul>") for script, data in sorted_pairs(raw_data) do if type(data) == "string" and m_sc_getByCode(script) then insert(module_info, ("<li><code>%s</code>: [[Module:%s]]</li>"):format(script, data)) end end insert(module_info, "</ul>") add = size(module_info) > 2 end end if add then insert(ret, [=[ <tr> <th>]=] .. heading .. [=[</th> <td>]=] .. concat(module_info) .. [=[</td> </tr> ]=]) end end end add_module_info(raw_data.generate_forms, "Form-generating<br>module") add_module_info(raw_data.translit, "Modula<br>translîterasyonê") add_module_info(raw_data.display_text, "Display text<br>module") add_module_info(raw_data.entry_name, "Entry name<br>module") add_module_info(raw_data.sort_key, "Modula<br>rêzkirinê") local wikidataItem = lang:getWikidataItem() if lang:getWikidataItem() and mw.wikibase then local URL = mw.wikibase.getEntityUrl(wikidataItem) local link if URL then link = '[' .. URL .. ' ' .. wikidataItem .. ']' else link = '<span class="error">Invalid Wikidata item: <code>' .. wikidataItem .. '</code></span>' end insert(ret, "<tr><th>Wîkîdata</th><td>" .. link .. "</td></tr>") end insert(ret, "</table>") return concat(ret) end local function NavFrame(content, title) return '<div class="NavFrame"><div class="NavHead">' .. (title or '{{{title}}}') .. '</div>' .. '<div class="NavContent" style="text-align: left;">' .. content .. '</div></div>' end function export.country_categories(frame) end -- Category object local Category = {} Category.__index = Category function Category.new(info) for key, val in pairs(info) do if not (key == "code" or key == "entryname" or key == "setsister" or key == "setwiki" or key == "setwikt") then error("The parameter \"" .. key .. "\" was not recognized.") end end local self = setmetatable({}, Category) self._info = info if not self._info.code then error("Koda zimanî nehatiye nivîsîn.") else self._lang = require("Modul:languages").getByCode(self._info.code) or error("Koda zimanî \"" .. self._info.code .. "\" ne guncav e.") end return self end export.new = Category.new function Category:getInfo() return self._info end function Category:getBreadcrumbName() return self._lang:getCanonicalName() end function Category:getDataModule() return "Modul:dara kategoriyan/zimankatbike" end function Category:canBeEmpty() return false end function Category:isHidden() return false end function Category:getCategoryName() return mw.getContentLanguage():ucfirst(self._lang:getCategoryName()) end function Category:getDescription() if self._lang:getCode() == "und" then return "Kategoriya sereke ya '''" .. self._lang:getCategoryName() .. "''', represented in Wiktionary by the [[Wiktionary:Languages|code]] '''" .. self._lang:getCode() .. "'''. " .. "This language contains terms in historical writing, whose meaning has not yet been determined by scholars." end local canonicalName = self._lang:getCanonicalName() local ret = linkbox(self._lang, self._info.setwiki, self._info.setwikt, self._info.setsister, self._info.entryname) ret = ret .. "Ev ji bo zimanê '''" .. mw.ustring.lower(self._lang:getCategoryName()) .. "''' kategoriya sereke ye.\n\nDanasîna zimanî:\n\n" .. infobox(self._lang) if self._lang:hasType("reconstructed") then ret = ret .. "\n\n" .. mw.getContentLanguage():ucfirst(canonicalName) .. " zimanekî ji nû ve sazkirî ye. Lewra peyvên di vê rûpelê de yekser nehatine peyitandin." .. " Lê belê li gorî rêbaza berawirdkirinê piştraskirî ne û tên bawerkirin ku di wextekî de hebûn.\n\n" .. "Peyvên bi zimanê " .. canonicalName .. " nabe ku cihê xwe valahiya nav ya sereke de bigrin, van peyvan divê bi [[Wîkîferheng:Jinûvesazî|'''Jinûvesazî:''']] dest pê bikin." -- elseif self._lang:hasType("appendix-constructed") then -- ret = ret .. "\n\n" .. -- ucfirst(canonicalName) .. " zimanekî çêkirî ye. Loma " .. -- "divê madeyên bi " .. canonicalName .. -- " di valahiya nav a Pêvek: de werin qeydkirin. " .. -- "Ji bo hemû madeyan binêre [[Pêvek:" .. ucfirst(canonicalName) .. "]]." end ret = ret .. " __EXPECTUNUSEDCATEGORY__" -- it's OK if this category is empty return ret end function Category:getParents() local canonicalName = self._lang:getCanonicalName() local ret = {{name = "Kategorî:Hemû ziman", sort = canonicalName}} local fam = self._lang:getFamily() local famCode = fam and fam:getCode() if not fam then table.insert(ret, {name = "Kategorî:Zimanên ku nehatine sinifandin", sort = canonicalName}) elseif famCode == "qfa-iso" then table.insert(ret, {name = "Kategorî:Zimanên îzole", sort = canonicalName}) elseif famCode == "qfa-mix" then table.insert(ret, {name = "Kategorî:Zimanên têkilkirî", sort = canonicalName}) elseif famCode == "sgn" then table.insert(ret, {name = "Kategorî:Zimanên destan", sort = canonicalName}) elseif famCode == "crp" then table.insert(ret, {name = "Kategorî:Zimanên kreyolî yan pîdgînî", sort = canonicalName}) for _, anc in ipairs(self._lang:getAncestors()) do table.insert(ret, {name = "Kategorî:Zimanên kreyolî yan pîdgînî li ser bingeha " .. anc:getCanonicalName(), sort = canonicalName}) end elseif famCode == "art" then table.insert(ret, {name = "Kategorî:Zimanên çêkirî", sort = canonicalName}) -- if self._lang:hasType("appendix-constructed") then -- table.insert(ret, {name = "Kategorî:Zimanên çêkirî di pêvekan de", sort = canonicalName}) -- else -- table.insert(ret, {name = "Kategorî:Zimanên çêkirî", sort = canonicalName}) -- end for _, anc in ipairs(self._lang:getAncestors()) do table.insert(ret, {name = "Kategorî:Li ser bingeha " .. anc:getCanonicalName() .. " zimanên çêkirî", sort = canonicalName}) end else table.insert(ret, {name = "Kategorî:" .. mw.getContentLanguage():ucfirst(fam:getCategoryName()), sort = canonicalName}) if self._lang:hasType("reconstructed") then table.insert(ret, {name = "Kategorî:Zimanên jinûvesazkirî", sort = (mw.ustring.gsub(canonicalName, "^Proto%-", ""))}) end end if self._lang:hasTranslit() then table.insert(ret, {name = "Kategorî:Zimanên bi transliterasyona xweser", sort = canonicalName}) end return ret end function Category:getUmbrella() return nil end return export g4y0jrfqlopkoww2vxhbjzh6geo06x6 6291621 6291619 2026-07-26T23:34:49Z Wikihez 7344 6291621 Scribunto text/plain local export = {} local new_title = mw.title.new local ucfirst = require("Module:string utilities").ucfirst local split = require("Module:string utilities").split local m_languages = require("Module:languages") local m_sc_getByCode = require("Module:scripts").getByCode local m_table = require("Module:table") local parse_utilities_module = "Module:parse utilities" local concat = table.concat local insert = table.insert local reverse_ipairs = m_table.reverseIpairs local serial_comma_join = m_table.serialCommaJoin local size = m_table.size local sorted_pairs = m_table.sortedPairs local to_json = require("Module:JSON").toJSON local Hang = m_sc_getByCode("Hang") local Hani = m_sc_getByCode("Hani") local Hira = m_sc_getByCode("Hira") local Hrkt = m_sc_getByCode("Hrkt") local Kana = m_sc_getByCode("Kana") local function makeCategoryLink(object) return "[[:Kategorî:" .. object:getCategoryName() .. "|" .. object:getCanonicalName() .. "]]" end local function linkbox(lang, setwiki, setwikt, setsister, entryname) local wiktionarylinks = "''Tine.''" local canonicalName = lang:getCanonicalName() local wikimediaLanguages = lang:getWikimediaLanguages() local categoryName = lang:getCategoryName() local categoryName = mw.ustring.lower( categoryName ) local wikipediaArticle = setwiki or lang:getWikipediaArticle() if setwikt then require "Modul:debug".track "langcatboiler/setwikt" if setwikt == "-" then require "Modul:debug".track "langcatboiler/setwikt/hyphen" end end if setwikt ~= "-" and wikimediaLanguages and wikimediaLanguages[1] then wiktionarylinks = {} for _, wikimedialang in ipairs(wikimediaLanguages) do table.insert(wiktionarylinks, (wikimedialang:getCanonicalName() ~= canonicalName and "(''" .. wikimedialang:getCanonicalName() .. "'') " or "") .. "'''[[:" .. wikimedialang:getCode() .. ":|" .. wikimedialang:getCode() .. ".wiktionary.org]]'''") end wiktionarylinks = table.concat(wiktionarylinks, "<br/>") end local plural = wikimediaLanguages[2] and "s" or "" return table.concat{ [=[<div class="tablo-flip standard-box" style="margin: 0 0 .5em 1em; width: 270px;"> <div style="text-align: center; margin-bottom: 10px; margin-top: 5px">'''Zimanê ]=], categoryName, [=['''</div> {| style="font-size: 90%; var(--wikt-palette-grey-4, lightgray);;" |- | style="vertical-align: middle; height: 35px; width: 35px;" | [[File:Wiktionary-logo-v2.svg|35px|none|Wiktionary]] || ''Çapa ]=], categoryName, [=[ ya Wîkîferhengê'' |- | colspan="2" style="padding-left: 10px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | ]=], wiktionarylinks, [=[ |- | style="vertical-align: middle; height: 35px" | [[File:Wikipedia-logo.png|35px|none|Wikipedia]] || ''Meqaleya Wîkîpediyê ya li ser ]=], categoryName, [=['' |- | colspan="2" style="padding-left: 10px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);;" | ]=], (setwiki == "-" and "''Tine.''" or "'''[[w:" .. wikipediaArticle .. "|" .. wikipediaArticle .. "]]'''"), [=[ |- | style="vertical-align: middle; height: 35px" | [[File:Open book nae 02.svg|35px|none|Made]] || Madeya Wîkîferhengê ya li ser ]=], categoryName,[=[ |- | colspan="2" style="padding-left: 10px;" | ''']=], require("Modul:links").full_link({lang = require("Modul:languages").getByCode("ku"), term = categoryName}), [=[''' |} </div>]=] } end local function edit_link(title, text) return '<span class="plainlinks">[' .. tostring(mw.uri.fullUrl(title, { action = "edit" })) .. ' ' .. text .. ']</span>' end -- Should perhaps use wiki syntax. local function infobox(lang) local ret = {} insert(ret, '<table class="wikitable language-category-info"') local raw_data = lang:getData("extra") if raw_data then local replacements = { [1] = "canonical-name", [2] = "wikidata-item", [3] = "family", [4] = "scripts", } local function replacer(letter1, letter2) return letter1:lower() .. "-" .. letter2:lower() end -- For each key in the language data modules, returns a descriptive -- kebab-case version (containing ASCII lowercase words separated -- by hyphens). local function kebab_case(key) key = replacements[key] or key key = key:gsub("(%l)(%u)", replacer):gsub("(%l)_(%l)", replacer) return key end local compress = {compress = true} local function html_attribute_encode(str) str = to_json(str, compress) :gsub('"', "&quot;") -- & in attributes is automatically escaped. -- :gsub("&", "&amp;") :gsub("<", "&lt;") :gsub(">", "&gt;") return str end insert(ret, ' data-code="' .. lang:getCode() .. '"') for k, v in sorted_pairs(raw_data) do insert(ret, " data-" .. kebab_case(k) .. '="' .. html_attribute_encode(v) .. '"') end end insert(ret, '>\n') insert(ret, '<tr class="language-category-data">\n<th colspan="2">' .. edit_link(lang:getDataModuleName(), "Datayên vî zimanî biguhêre") .. "</th>\n</tr>\n") insert(ret, "<tr>\n<th>Navê zimanî</th><td>" .. ucfirst(lang:getCanonicalName()) .. "</td>\n</tr>\n") local otherNames = lang:getOtherNames() if otherNames then local names = {} for _, name in ipairs(otherNames) do insert(names, "<li>" .. ucfirst(name) .. "</li>") end if #names > 0 then insert(ret, "<tr>\n<th>Navên din</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end local aliases = lang:getAliases() if aliases then local names = {} for _, name in ipairs(aliases) do insert(names, "<li>" ..ucfirst(name) .. "</li>") end if #names > 0 then insert(ret, "<tr>\n<th>Bernav</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end local varieties = lang:getVarieties() if varieties then local names = {} for _, name in ipairs(varieties) do if type(name) == "string" then insert(names, "<li>" .. ucfirst(name) .. "</li>") else assert(type(name) == "table") local first_var local subvars = {} for i, var in ipairs(name) do if i == 1 then first_var = var else insert(subvars, "<li>" .. ucfirst(var) .. "</li>") end end if #subvars > 0 then insert(names, "<li><dl><dt>" .. first_var .. "</dt>\n<dd><ul>" .. concat(subvars, "\n") .. "</ul></dd></dl></li>") elseif first_var then insert(names, "<li>" .. first_var .. "</li>") end end end if #names > 0 then insert(ret, "<tr>\n<th>Varyant</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n") end end insert(ret, "<tr>\n<th>[[WF:LZ|Koda zimanî]]</th><td><code>" .. lang:getCode() .. "</code></td>\n</tr>\n") insert(ret, "<tr>\n<th>[[WF:Lîsteya malbatan|Malbata zimanî]]</th>\n") local fam = lang:getFamily() local famCode = fam and fam:getCode() if not fam then insert(ret, "<td>nehatiye sinifandin</td>") elseif famCode == "qfa-iso" then insert(ret, "<td>[[:Kategorî:Zimanên îzole|Zimanekî îzole]]</td>") elseif famCode == "qfa-mix" then insert(ret, "<td>[[:Kategorî:Zimanên têkilkirî|Zimanekî têkilkirî]]</td>") elseif famCode == "sgn" then insert(ret, "<td>[[:Kategorî:Zimanên destan|Zimanê destan]]</td>") elseif famCode == "crp" then insert(ret, "<td>[[:Kategorî:Zimanên kreyolî yan pîdgînî|Kreyolî yan pîdgînî]]</td>") elseif famCode == "art" then insert(ret, "<td>[[:Kategorî:Zimanên çêkirî|Zimanekî çêkirî]]</td>") else insert(ret, "<td>" .. ucfirst(fam:makeCategoryLink()) .. "</td>") end insert(ret, "\n</tr>\n<tr>\n<th>Zimanên pêşî</th>\n<td>") local ancestors = lang:getAncestors() if ancestors[2] then local ancestorList = {} for i, anc in ipairs(ancestors) do ancestorList[i] = "<li>" .. anc:makeCategoryLink() .. "</li>" end insert(ret, "<ul>\n" .. concat(ancestorList, "\n") .. "</ul>") else local ancestorChain = lang:getAncestorChainOld() if ancestorChain[1] then local chain = {} for _, anc in reverse_ipairs(ancestorChain) do insert(chain, "<li>" .. anc:makeCategoryLink() .. "</li>") end insert(ret, "<ul>\n" .. concat(chain, "\n<ul>\n") .. ("</ul>"):rep(#chain)) else insert(ret, "unknown") end end insert(ret, "</td>\n</tr>\n") local scripts = lang:getScripts() if scripts[1] then local script_text = {} local function makeScriptLine(sc) local code = sc:getCode() local url = tostring(mw.uri.fullUrl('Special:Search', { search = 'contentmodel:css insource:"' .. code .. '" insource:/\\.' .. code .. '/', ns8 = '1' })) return sc:getCanonicalName() .. ' (<span class="plainlinks" title="Search for stylesheets referencing this script">[' .. url .. ' <code>' .. code .. '</code>]</span>)' end local function add_Hrkt(text) insert(text, "<li>" .. makeScriptLine(Hrkt)) insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hira) .. "</li>") insert(text, "<li>" .. makeScriptLine(Kana) .. "</li>") insert(text, "</ul>") insert(text, "</li>") end for _, sc in ipairs(scripts) do local text = {} local code = sc:getCode() if code == "Hrkt" then add_Hrkt(text) else insert(text, "<li>" .. makeScriptLine(sc)) if code == "Jpan" then insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>") add_Hrkt(text) insert(text, "</ul>") elseif code == "Kore" then insert(text, "<ul>") insert(text, "<li>" .. makeScriptLine(Hang) .. "</li>") insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>") insert(text, "</ul>") end insert(text, "</li>") end insert(script_text, concat(text, "\n")) end insert(ret, "<tr>\n<th>[[Modul:scripts/data|Skrîpt]]</th>\n<td><ul>\n" .. concat(script_text, "\n") .. "</ul></td>\n</tr>\n") else insert(ret, "<tr>\n<th>[[Modul:scripts/data|Skrîpt]]</th>\n<td>nehatiye nivîsin</td>\n</tr>\n") end local function add_module_info(raw_data, heading) if raw_data then local scripts = lang:getScriptCodes() local module_info, add = {}, false if type(raw_data) == "string" then insert(module_info, ("[[Module:%s]]"):format(raw_data)) add = true else local raw_data_type = type(raw_data) if raw_data_type == "table" and size(scripts) == 1 and type(raw_data[scripts[1]]) == "string" then insert(module_info, ("[[Module:%s]]"):format(raw_data[scripts[1]])) add = true elseif raw_data_type == "table" then insert(module_info, "<ul>") for script, data in sorted_pairs(raw_data) do if type(data) == "string" and m_sc_getByCode(script) then insert(module_info, ("<li><code>%s</code>: [[Module:%s]]</li>"):format(script, data)) end end insert(module_info, "</ul>") add = size(module_info) > 2 end end if add then insert(ret, [=[ <tr> <th>]=] .. heading .. [=[</th> <td>]=] .. concat(module_info) .. [=[</td> </tr> ]=]) end end end add_module_info(raw_data.generate_forms, "Form-generating<br>module") add_module_info(raw_data.translit, "Modula<br>translîterasyonê") add_module_info(raw_data.display_text, "Display text<br>module") add_module_info(raw_data.entry_name, "Entry name<br>module") add_module_info(raw_data.sort_key, "Modula<br>rêzkirinê") local wikidataItem = lang:getWikidataItem() if lang:getWikidataItem() and mw.wikibase then local URL = mw.wikibase.getEntityUrl(wikidataItem) local link if URL then link = '[' .. URL .. ' ' .. wikidataItem .. ']' else link = '<span class="error">Invalid Wikidata item: <code>' .. wikidataItem .. '</code></span>' end insert(ret, "<tr><th>Wîkîdata</th><td>" .. link .. "</td></tr>") end insert(ret, "</table>") return concat(ret) end local function NavFrame(content, title) return '<div class="NavFrame"><div class="NavHead">' .. (title or '{{{title}}}') .. '</div>' .. '<div class="NavContent" style="text-align: left;">' .. content .. '</div></div>' end function export.country_categories(frame) end -- Category object local Category = {} Category.__index = Category function Category.new(info) for key, val in pairs(info) do if not (key == "code" or key == "entryname" or key == "setsister" or key == "setwiki" or key == "setwikt") then error("The parameter \"" .. key .. "\" was not recognized.") end end local self = setmetatable({}, Category) self._info = info if not self._info.code then error("Koda zimanî nehatiye nivîsîn.") else self._lang = require("Modul:languages").getByCode(self._info.code) or error("Koda zimanî \"" .. self._info.code .. "\" ne guncav e.") end return self end export.new = Category.new function Category:getInfo() return self._info end function Category:getBreadcrumbName() return self._lang:getCanonicalName() end function Category:getDataModule() return "Modul:dara kategoriyan/zimankatbike" end function Category:canBeEmpty() return false end function Category:isHidden() return false end function Category:getCategoryName() return mw.getContentLanguage():ucfirst(self._lang:getCategoryName()) end function Category:getDescription() if self._lang:getCode() == "und" then return "Kategoriya sereke ya '''" .. self._lang:getCategoryName() .. "''', represented in Wiktionary by the [[Wiktionary:Languages|code]] '''" .. self._lang:getCode() .. "'''. " .. "This language contains terms in historical writing, whose meaning has not yet been determined by scholars." end local canonicalName = self._lang:getCanonicalName() local ret = linkbox(self._lang, self._info.setwiki, self._info.setwikt, self._info.setsister, self._info.entryname) ret = ret .. "Ev ji bo zimanê '''" .. mw.ustring.lower(self._lang:getCategoryName()) .. "''' kategoriya sereke ye.\n\nDanasîna zimanî:\n\n" .. infobox(self._lang) if self._lang:hasType("reconstructed") then ret = ret .. "\n\n" .. mw.getContentLanguage():ucfirst(canonicalName) .. " zimanekî ji nû ve sazkirî ye. Lewra peyvên di vê rûpelê de yekser nehatine peyitandin." .. " Lê belê li gorî rêbaza berawirdkirinê piştraskirî ne û tên bawerkirin ku di wextekî de hebûn.\n\n" .. "Peyvên bi zimanê " .. canonicalName .. " nabe ku cihê xwe valahiya nav ya sereke de bigrin, van peyvan divê bi [[Wîkîferheng:Jinûvesazî|'''Jinûvesazî:''']] dest pê bikin." -- elseif self._lang:hasType("appendix-constructed") then -- ret = ret .. "\n\n" .. -- ucfirst(canonicalName) .. " zimanekî çêkirî ye. Loma " .. -- "divê madeyên bi " .. canonicalName .. -- " di valahiya nav a Pêvek: de werin qeydkirin. " .. -- "Ji bo hemû madeyan binêre [[Pêvek:" .. ucfirst(canonicalName) .. "]]." end ret = ret .. " __EXPECTUNUSEDCATEGORY__" -- it's OK if this category is empty return ret end function Category:getParents() local canonicalName = self._lang:getCanonicalName() local ret = {{name = "Kategorî:Hemû ziman", sort = canonicalName}} local fam = self._lang:getFamily() local famCode = fam and fam:getCode() if not fam then table.insert(ret, {name = "Kategorî:Zimanên ku nehatine sinifandin", sort = canonicalName}) elseif famCode == "qfa-iso" then table.insert(ret, {name = "Kategorî:Zimanên îzole", sort = canonicalName}) elseif famCode == "qfa-mix" then table.insert(ret, {name = "Kategorî:Zimanên têkilkirî", sort = canonicalName}) elseif famCode == "sgn" then table.insert(ret, {name = "Kategorî:Zimanên destan", sort = canonicalName}) elseif famCode == "crp" then table.insert(ret, {name = "Kategorî:Zimanên kreyolî yan pîdgînî", sort = canonicalName}) for _, anc in ipairs(self._lang:getAncestors()) do table.insert(ret, {name = "Kategorî:Zimanên kreyolî yan pîdgînî li ser bingeha " .. anc:getCanonicalName(), sort = canonicalName}) end elseif famCode == "art" then table.insert(ret, {name = "Kategorî:Zimanên çêkirî", sort = canonicalName}) -- if self._lang:hasType("appendix-constructed") then -- table.insert(ret, {name = "Kategorî:Zimanên çêkirî di pêvekan de", sort = canonicalName}) -- else -- table.insert(ret, {name = "Kategorî:Zimanên çêkirî", sort = canonicalName}) -- end for _, anc in ipairs(self._lang:getAncestors()) do table.insert(ret, {name = "Kategorî:Li ser bingeha " .. anc:getCanonicalName() .. " zimanên çêkirî", sort = canonicalName}) end else table.insert(ret, {name = "Kategorî:" .. mw.getContentLanguage():ucfirst(fam:getCategoryName()), sort = canonicalName}) if self._lang:hasType("reconstructed") then table.insert(ret, {name = "Kategorî:Zimanên jinûvesazkirî", sort = (mw.ustring.gsub(canonicalName, "^Proto%-", ""))}) end end if self._lang:hasTranslit() then table.insert(ret, {name = "Kategorî:Zimanên bi transliterasyona xweser", sort = canonicalName}) end return ret end function Category:getUmbrella() return nil end return export 8ys9rh5ahih86x3hgwdhi95ubijl7e4 Modul:kurterê 828 907825 6291624 6072019 2026-07-26T23:48:07Z Wikihez 7344 6291624 Scribunto text/plain local export = {} function export.raber(frame)--show local output = {} -- Hacky way to allow function to be called from other modules. local args = (frame.getParent and frame:getParent().args) or frame local demo = false local FULLPAGENAME = mw.title.getCurrentTitle().fullText local BASEPAGENAME, SUBPAGENAME = FULLPAGENAME:match("^(.-)/([^/]-)$") -- BASEPAGENAME = BASEPAGENAME or FULLPAGENAME if frame.getParent and FULLPAGENAME == frame:getParent():getTitle() then demo = true end output[#output + 1] = '<table class="noprint plainlinks shortcutbox tablo-flip" style="border: 1px solid var(--wikt-palette-grey-4, #aaa); text-align: center; background: var(--wikt-palette-grey-4, lightgray);"><tr><th style="border: none; var(--wikt-palette-grey-4, lightgray);"><small>' if args.temp then output[#output + 1] = "[[kurterê|Kurterêya Demkî" .. (args[2] and 's' or '') .. "]]:<br/>" else output[#output + 1] = "[[kurterê|Kurterê" .. (args[2] and '' or '') .. "]]:<br/>" end local attn = false for i, shortcut in ipairs(args) do local title = mw.title.new(shortcut) if not title then output[#output + 1] = '<span class="attentionseeking">(<code>' .. mw.text.nowiki(shortcut) .. '</code>)</span><br/>' attn = true else local iattn = false local item if not title.exists then attn = true --preload naxebite nizanim çima. Kes dizane? local query = 'action=edit&redlink=1&preloadtext=' .. mw.uri.encode('#BERALÎKIRIN [[' .. FULLPAGENAME .. ']]') if title.nsText == "Şablon" then item = '<code>{{[' .. tostring(mw.uri.fullUrl(shortcut, query)) .. ' <span style="color:#c20;">' .. title.text .. '</span>]}}</code>' else item = '[' .. tostring(mw.uri.fullUrl(shortcut, query)) .. ' <span style="color:#c20;">' .. shortcut .. '</span>]' end else if title.nsText == "Şablon" then item = '<code>{{[' .. tostring(mw.uri.fullUrl(shortcut, 'redirect=no')) .. ' ' .. title.text .. ']}}</code>' else item = '[' .. tostring(mw.uri.fullUrl(shortcut, 'redirect=no')) .. ' ' .. shortcut .. ']' end if not demo then local redirectTarget = title.redirectTarget if redirectTarget then redirectTarget.fragment = '' -- remove fragment, if present local targetText = redirectTarget.fullText if title.nsText == "Şablon" then if SUBPAGENAME == 'belge' then iattn = (targetText ~= BASEPAGENAME) else iattn = (targetText ~= FULLPAGENAME) end else if SUBPAGENAME == 'belge' then iattn = not (targetText == BASEPAGENAME or targetText == FULLPAGENAME) else iattn = not (targetText == FULLPAGENAME or targetText == FULLPAGENAME .. '/belge') end end end if not (redirectTarget and redirectTarget.exists) then iattn = true end end end if iattn then item = '<span class="attentionseeking">' .. item .. '</span>' end output[#output + 1] = item .. '<br/>' attn = attn or iattn end end if not args.nocat then if attn then output[#output + 1] = '[[Kategorî:Daxwazên lênêrîna kurterêyan]]' end if SUBPAGENAME ~= 'belge' then output[#output + 1] = '[[Kategorî:Rûpelên Wîkîferhengê bi kurterêyan]]' end end output[#output + 1] = '</small></th></tr></table>' return table.concat(output) end return export grs7kijg557uicds5n98zk342amogk8 6291625 6291624 2026-07-26T23:51:51Z Wikihez 7344 6291625 Scribunto text/plain local export = {} function export.raber(frame)--show local output = {} -- Hacky way to allow function to be called from other modules. local args = (frame.getParent and frame:getParent().args) or frame local demo = false local FULLPAGENAME = mw.title.getCurrentTitle().fullText local BASEPAGENAME, SUBPAGENAME = FULLPAGENAME:match("^(.-)/([^/]-)$") -- BASEPAGENAME = BASEPAGENAME or FULLPAGENAME if frame.getParent and FULLPAGENAME == frame:getParent():getTitle() then demo = true end output[#output + 1] = '<table class="noprint plainlinks shortcutbox tablo-flip" style="border: 1px solid var(--wikt-palette-grey-4, #aaa); text-align: center; background: var(--wikt-palette-grey-4, #fff) background:transparent;;"><tr><th style="border: none; "><small>' if args.temp then output[#output + 1] = "[[kurterê|Kurterêya Demkî" .. (args[2] and 's' or '') .. "]]:<br/>" else output[#output + 1] = "[[kurterê|Kurterê" .. (args[2] and '' or '') .. "]]:<br/>" end local attn = false for i, shortcut in ipairs(args) do local title = mw.title.new(shortcut) if not title then output[#output + 1] = '<span class="attentionseeking">(<code>' .. mw.text.nowiki(shortcut) .. '</code>)</span><br/>' attn = true else local iattn = false local item if not title.exists then attn = true --preload naxebite nizanim çima. Kes dizane? local query = 'action=edit&redlink=1&preloadtext=' .. mw.uri.encode('#BERALÎKIRIN [[' .. FULLPAGENAME .. ']]') if title.nsText == "Şablon" then item = '<code>{{[' .. tostring(mw.uri.fullUrl(shortcut, query)) .. ' <span style="color:#c20;">' .. title.text .. '</span>]}}</code>' else item = '[' .. tostring(mw.uri.fullUrl(shortcut, query)) .. ' <span style="color:#c20;">' .. shortcut .. '</span>]' end else if title.nsText == "Şablon" then item = '<code>{{[' .. tostring(mw.uri.fullUrl(shortcut, 'redirect=no')) .. ' ' .. title.text .. ']}}</code>' else item = '[' .. tostring(mw.uri.fullUrl(shortcut, 'redirect=no')) .. ' ' .. shortcut .. ']' end if not demo then local redirectTarget = title.redirectTarget if redirectTarget then redirectTarget.fragment = '' -- remove fragment, if present local targetText = redirectTarget.fullText if title.nsText == "Şablon" then if SUBPAGENAME == 'belge' then iattn = (targetText ~= BASEPAGENAME) else iattn = (targetText ~= FULLPAGENAME) end else if SUBPAGENAME == 'belge' then iattn = not (targetText == BASEPAGENAME or targetText == FULLPAGENAME) else iattn = not (targetText == FULLPAGENAME or targetText == FULLPAGENAME .. '/belge') end end end if not (redirectTarget and redirectTarget.exists) then iattn = true end end end if iattn then item = '<span class="attentionseeking">' .. item .. '</span>' end output[#output + 1] = item .. '<br/>' attn = attn or iattn end end if not args.nocat then if attn then output[#output + 1] = '[[Kategorî:Daxwazên lênêrîna kurterêyan]]' end if SUBPAGENAME ~= 'belge' then output[#output + 1] = '[[Kategorî:Rûpelên Wîkîferhengê bi kurterêyan]]' end end output[#output + 1] = '</small></th></tr></table>' return table.concat(output) end return export 1uwvkxui7vikn82bccwv75gdsj6gxwm Bikarhêner:Wikihez/Gadget-linkify.js 2 974481 6291568 6276928 2026-07-26T15:02:59Z Wikihez 7344 6291568 javascript text/javascript // MediaWiki gadget: automatically link selected text to existing wiktionary entries (function($) { var messages = { ku: { toolbarButtonName: "Li nivîsa bijartî bi otomatîkî wîkîlînk lê zêde bike", delinkifyButtonName: "Ji nivîsa bijartî wîkîlînkan rake", caseChangeLabel: "Nivîsa bijartî MEZIN/biçûk bike", caseUpperLabel: "HEMÛ MEZIN", caseLowerLabel: "hemû biçûk", caseSentenceLabel: "Wekî cimleyan", caseTitleLabel: "Herfa Ewil Mezin" }, en: { toolbarButtonName: "Automatically link selected text", delinkifyButtonName: "Remove links from selected text", caseChangeLabel: "Change letter case", caseUpperLabel: "UPPERCASE", caseLowerLabel: "lowercase", caseSentenceLabel: "Sentence case", caseTitleLabel: "Title Case" }, es: { toolbarButtonName: "Automáticamente insertar enlaces en la selección", delinkifyButtonName: "Quitar enlaces del texto seleccionado", caseChangeLabel: "Cambiar mayúsculas/minúsculas", caseUpperLabel: "MAYÚSCULAS", caseLowerLabel: "minúsculas", caseSentenceLabel: "Case de oración", caseTitleLabel: "Case de Título" } }; // Kurmanji function words that should never become links on their own, // plus the 8 bare Kurmanji vowels alone. var ignoredWordList = ( "a|an|ba|bê|bi|çi|çu|çû|da|de|dê|dî|di|e|ê|ên|erê|ev|ew|em|gel|ha|hê|her|" + "hûn|i|î|jê|jî|ka|kî|ku|kû|lê|li|me|min|na|ne|o|pê|re|ra|ti|te|tê|" + "tu|u|û|va|van|ve|vê|vî|wan|we|wê|wî|wir|vir|ya|yan|yê|yên" ).split("|"); var ignoredWords = {}; $.each(ignoredWordList, function(i, word) { ignoredWords[word] = true; }); /** * One less than the maximum number of syllables to be considered as a compound * word. */ var lookahead = 2; // Token boundary: uppercase Latin A-Z, Kurmanji diacritic letters (Ç ê Î Ş Û), // combining marks, and wiki markup delimiters. var tokenRE = /(\s*(?:<!--[\s\S]+?-->|\{\{[\s\S]+?\}\}|\[\[.+?\]\]|[^A-ZÇÊÎŞÛçêîşû\u0300-\u036F{[< ]+|[{[<])\s*)/i; // Matches the "{{ziman|ku}}" language-section template that marks an // entry as actually having a Kurdish section. Templates are written by // hand, so be forgiving of stray whitespace around the pipe/braces, but // don't try to handle template redirects/aliases here. var kuZimanRE = /\{\{\s*ziman\s*\|\s*ku\s*\}\}/i; // "=== Formeke navdêrê ===" but also allow a numbered variant, e.g. // "=== Formeke navdêrê 2 ===", which shows up when a page has more than // one noun-form section. var kuFormekeNavdereRE = /^\s*=+\s*Formeke navdêrê(?:\s+\d+)?\s*=+\s*$/im; String.prototype.linkifyUncapitalize = function() { return this[0].toLowerCase() + this.substr(1); }; // Kurmanji noun-declension suffixes, longest first so the greedy match // prefers the most specific ending. Generous rather than precise: every // candidate root is verified against real wiktionary entries before use, // so a bad strip is just discarded, never turned into a wrong link. var nounSuffixes = [ "iyekî", "iyekê", "iyeke", "yekî", "yekê", "yeke", "ekî", "ekê", "eke", "iyên", "iyan", "iyek", "iyê", "iya", "yên", "yan", "yek", "yî", "yê", "ya", "ek", "ên", "an", "a", "ê", "î", ]; /** * Given a lowercase surface word, returns candidate noun roots obtained by * stripping a known declension suffix -- e.g. "rûpeleke" -> ["rûpel"]. * Longest remaining root first, so safer candidates are tried first. */ function getNounStemCandidates(word) { var candidates = []; for (var i = 0; i < nounSuffixes.length; i++) { var suf = nounSuffixes[i]; // Require a 2+ character root, to avoid degenerate single letters. if (word.length > suf.length + 1 && word.slice(-suf.length) === suf) { candidates.push(word.slice(0, -suf.length)); } } // "giranî" + "yê" -> "giraniyê" drops the circumflex, so stripping "yê" // gives "girani" not "giranî". Restore the î in place -- don't keep // "girani" around too, it's invalid Kurdish orthography and would just // be dead weight in the existence check. candidates = candidates.map(function(root) { if (root.length > 1 && root.slice(-1) === "i") { return root.slice(0, -1) + "î"; } return root; }); candidates.sort(function(a, b) { return b.length - a.length; }); return candidates; } // Words this short are too ambiguous / noisy to bother linking, even if // they aren't in ignoredWords and even if a real entry exists for them. var MIN_LINKABLE_LENGTH = 3; // MediaWiki batches titles up to 50 per request for normal users (500 for // bots). Used both for the stem-confirmation content check and for the // {{ziman|ku}} content check below. var TITLE_BATCH_SIZE = 50; /** * Splits an array of titles into chunks of TITLE_BATCH_SIZE, so a single * selection with a lot of unique words never trips the API's per-request * titles limit. */ function chunkTitles(titles) { var chunks = []; for (var i = 0; i < titles.length; i += TITLE_BATCH_SIZE) { chunks.push(titles.slice(i, i + TITLE_BATCH_SIZE)); } return chunks; } var userLang = mw.config.get("wgUserLanguage"); function getMessage(name) { return (messages[userLang] || messages.ku)[name] || messages.ku[name]; } $.kuLinkify = function(text, callback, debug) { var tokens = text.split(tokenRE); var displayTokens = []; var words = {}; var existingLinks = {}; // Surface words (lowercase-first-letter form) that have already been // assigned a link during this run -- prevents the same word from being // linked twice, even if it resolves to a different target (stem vs. // direct entry vs. redlink) on a later occurrence. var linkedWords = {}; // surface word -> candidate noun roots, e.g. stemCandidates["yekeya"] = ["yeke", "yekey"] // Pruned by processStemCheckResults() below: kept if the surface word has // no page of its own (nothing to contradict the stem), or if its page is // confirmed as an inflected form (has "Formeke navdêrê"). Deleted if its // page exists but ISN'T marked as a form -- that's a real headword, not // something to stem. var stemCandidates = {}; $.each(tokens, function(i, token) { if (debug) displayTokens.push(mw.html.escape(token)); var tokenLinks = token.match(/\[\[.+?(?:\|.+?)?\]\]/g); $.each(tokenLinks || [], function() { var parts = this.match(/\[\[(.+?)(?:\|(.+?))?\]\]/); var target = parts[1]; var display = parts[2] !== undefined ? parts[2] : target; existingLinks[target] = true; if (display) linkedWords[display.linkifyUncapitalize()] = true; }); if (!token || token.match(tokenRE)) return; var tokenWords = token.split(" "); $.each(tokenWords, function(j, word) { for (var k = 0; k < lookahead + 1 && tokenWords[j + k]; k++) { if (k) word += " " + tokenWords[j + k]; if (!ignoredWords[word]) words[word] = true; if (word[0] != word[0].toLowerCase()) { var lowerWord = word.linkifyUncapitalize(); if (!ignoredWords[lowerWord]) { words[lowerWord] = true; } } } }); // Collect noun-root candidates per single word (declension endings // don't span multi-word compounds). Provisional until checked // against page content below. $.each(tokenWords, function(j, word) { if (!word) return; var lower = word.linkifyUncapitalize(); var stems = getNounStemCandidates(lower); if (stems.length) { stemCandidates[word] = stems; } }); if (debug) { displayTokens[i] = "<b style='border: 1px solid gray;'>" + mw.html.escape(token) + "</b>"; } }); if (debug) { var list = "<pre id='linkify-tokens'>\n" + displayTokens.join("") + "\n</pre>"; $("#linkify-progress").html(list).dialog({ modal: true }); } mw.loader.using("mediawiki.api", function() { var api = new mw.Api(); // Words that MATCHED a declension-suffix pattern need a content // check before we trust the stem: // - own page exists AND has "Formeke navdêrê" -> confirmed form, use stem // - own page exists but NO "Formeke navdêrê" -> real headword, don't stem // - own page doesn't exist at all -> nothing to check against, // keep the stem candidate // and let it be validated // normally (does the STEM // exist?) in the main pass var stemCheckWords = []; $.each(stemCandidates, function(word) { stemCheckWords.push(word); }); function processStemCheckResults(formPageExists, confirmedForms, then) { $.each(stemCandidates, function(word) { if (formPageExists[word] && !confirmedForms[word]) { delete stemCandidates[word]; } }); $.each(stemCandidates, function(word, stems) { $.each(stems, function(s, stem) { if (!ignoredWords[stem]) words[stem] = true; }); }); then(); } /** * Given the set of page titles that exist (from prop=links * "exists"), fetches their content and keeps only the ones that * actually contain a {{ziman|ku}} section. A page "existing" only * means SOME wiki has an entry there -- e.g. "away" exists as an * English entry, so without this check "awayî" would get wrongly * linked to [[away]] instead of stemmed to [[awa]]. Batched the * same way as the stem-confirmation query above. */ function filterToKuEntries(existingTitles, then) { var titles = []; $.each(existingTitles, function(title) { titles.push(title); }); if (!titles.length) { then({}); return; } var kuLinks = {}; var chunks = chunkTitles(titles); var pending = chunks.length; function handleChunk(chunk) { api.get({ action: "query", prop: "revisions", rvprop: "content", rvslots: "main", titles: chunk.join("|"), format: "json" }).done(function(result) { var normMap = {}; if (result.query && result.query.normalized) { $.each(result.query.normalized, function(i, n) { normMap[n.to] = n.from; }); } var pages = (result.query && result.query.pages) || {}; $.each(pages, function(pageId, page) { if (!page.revisions || !page.revisions.length) return; var originalTitle = normMap[page.title] || page.title; var rev = page.revisions[0]; var content = (rev.slots && rev.slots.main && rev.slots.main["*"]) || rev["*"] || ""; if (kuZimanRE.test(content)) { kuLinks[originalTitle] = true; } }); }).fail(function(error) { // Content check failed for this chunk -- be // conservative and don't link any of its titles, // rather than risk linking to a non-Kurdish entry. console.warn(error); }).always(function() { pending--; if (pending === 0) then(kuLinks); }); } $.each(chunks, function(i, chunk) { handleChunk(chunk); }); } function runLinkExistenceCheck() { var uniqueWords = []; $.each(words, function(word) { if (!ignoredWords[word]) uniqueWords.push(word); }); if (debug) { var uniqueLinks = []; $.each(existingLinks, function(link) { uniqueLinks.push(link); }); var stemDebug = []; $.each(stemCandidates, function(word, stems) { stemDebug.push(word + " -> " + stems.join(", ")); }); var list = "<hr /><p>Words to search for:</p><p>" + uniqueWords + "</p>"; list += "<hr /><p>Words that already have links:</p><p>" + uniqueLinks + "</p>"; list += "<hr /><p>Stem candidates kept:</p><p>" + stemDebug.join("<br>") + "</p>"; $("#linkify-progress").append(list); } if (!uniqueWords.length) return; api.get({ action: "parse", text: "[[" + uniqueWords.join("]] [[") + "]]", prop: "links" }).done(function(results) { $("#linkify-progress").append("<hr />"); var displayLinks = []; var existsAnywhere = {}; $.each(results.parse.links, function() { if (debug && "exists" in this) displayLinks.push(this["*"]); if ("exists" in this) existsAnywhere[this["*"]] = true; }); if (debug) { $("#linkify-progress").append("<p>Existing entries (any language):</p><p>" + displayLinks + "</p>"); } // A page existing isn't enough -- confirm it actually has // a {{ziman|ku}} section before we're willing to link to // it. `links` from here on means "exists AND is Kurdish". filterToKuEntries(existsAnywhere, function(links) { if (debug) { var kuDisplayLinks = []; $.each(links, function(title) { kuDisplayLinks.push(title); }); $("#linkify-progress").append("<p>Confirmed {{ziman|ku}} entries:</p><p>" + kuDisplayLinks + "</p>"); } $.each(tokens, function(i, token) { if (!token || token.match(tokenRE)) return; var tokenWords = token.split(" "); for (var j = 0; j < tokenWords.length; j++) { if (!tokenWords[j]) continue; var word = []; for (var k = 0; k < lookahead + 1; k++) { word.push(tokenWords[j + k]); } for (var k = lookahead; k >= 0; k--) { var compoundWord = word.join(" "); if (k === 0) { var lowerSingle = compoundWord.linkifyUncapitalize(); // Never link words this short, even if a real // entry exists for them. if (compoundWord.length < MIN_LINKABLE_LENGTH) break; // Only link the first occurrence of a given // surface word; skip repeats. if (linkedWords[lowerSingle]) break; var stems = stemCandidates[compoundWord] || stemCandidates[lowerSingle] || []; // 1. Confirmed form, or no own page -- try the stem. var stemLinked = false; for (var s = 0; s < stems.length; s++) { var stem = stems[s]; if ((stem in links) && !(stem in existingLinks) && !ignoredWords[stem]) { existingLinks[stem] = true; linkedWords[lowerSingle] = true; tokenWords[j] = "[[" + stem + "|" + tokenWords[j] + "]]"; stemLinked = true; break; } } if (stemLinked) break; if (!ignoredWords[compoundWord] && !ignoredWords[lowerSingle]) { // 2. Exact word (or lowercase form) is itself a real entry. var directTarget = null; if (compoundWord in links) { directTarget = compoundWord; } else if (lowerSingle in links) { directTarget = lowerSingle; } if (directTarget && !(directTarget in existingLinks)) { existingLinks[directTarget] = true; linkedWords[lowerSingle] = true; tokenWords[j] = "[[" + directTarget + "|" + tokenWords[j] + "]]"; } else if (!directTarget && !(compoundWord in existsAnywhere) && !(compoundWord in existingLinks)) { // 3. Doesn't exist anywhere yet -- self-link as a // redlink so it shows up as a page to create, // e.g. [[peyva|peyva]]. If it exists but only in // a non-Kurdish language, don't self-link either; // that would create a redlink over a page that's // actually occupied by another language's entry. existingLinks[compoundWord] = true; linkedWords[lowerSingle] = true; tokenWords[j] = "[[" + compoundWord + "|" + tokenWords[j] + "]]"; } } break; } if (compoundWord in links) { if ((compoundWord in existingLinks) || ignoredWords[compoundWord]) { j += k; break; } existingLinks[compoundWord] = true; linkedWords[compoundWord.linkifyUncapitalize()] = true; tokenWords[j] = "[[" + tokenWords[j]; tokenWords[j + k] += "]]"; j += k; break; } var lowerWord = compoundWord.linkifyUncapitalize(); if (lowerWord in links) { if ((lowerWord in existingLinks) || ignoredWords[lowerWord]) { j += k; break; } existingLinks[lowerWord] = true; linkedWords[lowerWord] = true; tokenWords[j] = "[[" + lowerWord + "|" + tokenWords[j]; tokenWords[j + k] += "]]"; j += k; break; } word.splice(-1, 1); } } tokens[i] = tokenWords.join(" "); if (debug) { displayTokens[i] = "<b style='border: 1px solid gray;'>" + mw.html.escape(tokens[i]) + "</b>"; } }); if (debug) { $("#linkify-tokens").html("\n" + displayTokens.join("") + "\n"); } callback(tokens.join("")); }); // end filterToKuEntries }).fail(function(error) { console.warn(error); callback(); }); } if (!stemCheckWords.length) { processStemCheckResults({}, {}, runLinkExistenceCheck); return; } // NOTE: MediaWiki batches titles up to 50 per request for normal // users (500 for bots). Chunk stemCheckWords here if it ever grows // past that for a single selection. api.get({ action: "query", prop: "revisions", rvprop: "content", rvslots: "main", titles: stemCheckWords.join("|"), format: "json" }).done(function(result) { var confirmedForms = {}; var formPageExists = {}; var normMap = {}; if (result.query && result.query.normalized) { $.each(result.query.normalized, function(i, n) { normMap[n.to] = n.from; }); } var pages = (result.query && result.query.pages) || {}; $.each(pages, function(pageId, page) { var originalWord = normMap[page.title] || page.title; if (!page.revisions || !page.revisions.length) { formPageExists[originalWord] = false; return; } formPageExists[originalWord] = true; var rev = page.revisions[0]; var content = (rev.slots && rev.slots.main && rev.slots.main["*"]) || rev["*"] || ""; if (kuFormekeNavdereRE.test(content)) { confirmedForms[originalWord] = true; } }); processStemCheckResults(formPageExists, confirmedForms, runLinkExistenceCheck); }).fail(function() { // Content check failed -- be conservative: treat every candidate // as if its page exists but isn't confirmed, so stemming is // skipped rather than risking a wrong link. var formPageExists = {}; $.each(stemCandidates, function(word) { formPageExists[word] = true; }); processStemCheckResults(formPageExists, {}, runLinkExistenceCheck); }); }); } // Matches a single wikilink: [[target]] or [[target|display]] (also // File:/Category: links, unwrapped the same way). var wikilinkRE = /\[\[([^\]|]*)\|([^\]]*)\]\]|\[\[([^\]|]*)\]\]/g; /** * Strips wikilink markup, keeping only the reader-facing text. * [[target|display]] -> display, [[target]] -> target. */ $.kuDelinkify = function(text) { return text.replace(wikilinkRE, function(match, pipedTarget, pipedDisplay, bareTarget) { return pipedDisplay !== undefined ? pipedDisplay : bareTarget; }); }; // --------------------------------------------------------------- // Letter-case conversion (UPPERCASE / lowercase / Sentence case / // Title Case). Kurmanji-only, so plain JS toUpperCase()/toLowerCase() // already do the right thing. Title Case reuses ignoredWords as the // stopword list for small grammatical words. // --------------------------------------------------------------- var KU_LETTER_CLASS = "a-zA-ZçêîşûÇÊÎŞÛ"; var kuNonLetterLeadingRE = new RegExp("^[^" + KU_LETTER_CLASS + "]*"); var kuNonLetterTrailingRE = new RegExp("[^" + KU_LETTER_CLASS + "]*$"); var kuSentenceBreakRE = new RegExp( "(^\\s*[a-zçêîşû]|[.!?][\"'\\)\\]]*\\s+[a-zçêîşû])", "gi" ); function kuCapitalizeFirstLetter(word) { if (word === "") return word; var lowered = word.toLowerCase(); return lowered.charAt(0).toUpperCase() + lowered.slice(1); } function kuToSentenceCase(text) { var lowered = text.toLowerCase(); return lowered.replace(kuSentenceBreakRE, function(match) { var idx = match.length - 1; return match.slice(0, idx) + match.charAt(idx).toUpperCase(); }); } // Capitalizes one word chunk: handles leading/trailing punctuation, // hyphenated compounds, and keeps ignoredWords lowercase unless forced // (first word, or right after an opening bracket/quote). function kuCapitalizeWordChunk(chunk, forceCapitalize) { var leading = (chunk.match(kuNonLetterLeadingRE) || [""])[0]; var trailing = (chunk.match(kuNonLetterTrailingRE) || [""])[0]; var core = chunk.slice(leading.length, chunk.length - trailing.length); if (core === "") return chunk; if (/[(["'‘“]/.test(leading)) forceCapitalize = true; var loweredCore = core.toLowerCase(); var isStopWord = !forceCapitalize && ignoredWords[loweredCore]; var newCore; if (isStopWord) { newCore = loweredCore; } else { newCore = core.split("-").map(function(part) { return kuCapitalizeFirstLetter(part); }).join("-"); } return leading + newCore + trailing; } function kuToTitleCase(text) { var wordIndex = 0; return text.split(/(\s+)/).map(function(chunk) { if (chunk === "" || /^\s+$/.test(chunk)) return chunk; var isFirstWord = wordIndex === 0; wordIndex++; return kuCapitalizeWordChunk(chunk, isFirstWord); }).join(""); } $.kuApplyCase = function(text, form) { switch (form) { case "upper": return text.toUpperCase(); case "lower": return text.toLowerCase(); case "sentence": return kuToSentenceCase(text); case "title": return kuToTitleCase(text); default: return text; } }; // --------------------------------------------------------------- // Dark-mode-aware toolbar icons. Vector 2022's day/night/os switch // marks its active theme with a class on <html> // (skin-theme-clientpref-{day,night,os}); when that class says "os", // or isn't present at all (older skins, no theme switcher), fall back // to the browser's own prefers-color-scheme signal. // --------------------------------------------------------------- var kuIcons = { wikiText: { normal: "//upload.wikimedia.org/wikipedia/commons/a/a0/OOjs_UI_icon_wikiText.svg", invert: "//upload.wikimedia.org/wikipedia/commons/f/fb/OOjs_UI_icon_wikiText-invert.svg" }, noWikiText: { normal: "//upload.wikimedia.org/wikipedia/commons/8/8c/OOjs_UI_icon_noWikiText-rtl.svg", invert: "//upload.wikimedia.org/wikipedia/commons/4/44/OOjs_UI_icon_noWikiText-rtl-invert.svg" }, searchCaseSensitive: { normal: "//upload.wikimedia.org/wikipedia/commons/e/e5/OOjs_UI_icon_searchCaseSensitive.svg", invert: "//upload.wikimedia.org/wikipedia/commons/0/02/OOjs_UI_icon_searchCaseSensitive-invert.svg" } }; function kuIsDarkMode() { var $html = $(document.documentElement); if ($html.hasClass("skin-theme-clientpref-night")) return true; if ($html.hasClass("skin-theme-clientpref-day")) return false; return !!(window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches); } function kuGetIcon(name) { var pair = kuIcons[name]; return kuIsDarkMode() ? pair.invert : pair.normal; } function getLinkifyTool() { return { type: "button", action: { type: "callback", execute: function() { mw.loader.using("jquery.textSelection", function() { var selection = $("#wpTextbox1").textSelection("getSelection"); $.kuLinkify(selection, function(linkedText) { if (!linkedText) return; $("#wpTextbox1").textSelection("encapsulateSelection", { peri: linkedText, replace: true }); }, $.kuLinkify_debug); }); } }, label: getMessage("toolbarButtonName"), icon: kuGetIcon("wikiText") }; } function getDelinkifyTool() { return { type: "button", action: { type: "callback", execute: function() { mw.loader.using("jquery.textSelection", function() { var selection = $("#wpTextbox1").textSelection("getSelection"); var plainText = $.kuDelinkify(selection); if (plainText === selection) return; $("#wpTextbox1").textSelection("encapsulateSelection", { peri: plainText, replace: true }); }); } }, label: getMessage("delinkifyButtonName"), icon: kuGetIcon("noWikiText") }; } // Applies a case transform to the current textbox selection in place. function applyCaseToSelection(form) { mw.loader.using("jquery.textSelection", function() { var selection = $("#wpTextbox1").textSelection("getSelection"); if (!selection) return; var converted = $.kuApplyCase(selection, form); if (converted === selection) return; $("#wpTextbox1").textSelection("encapsulateSelection", { peri: converted, replace: true }); }); } // Hand-built dropdown (wikiEditor "element" tool) instead of the built-in // "select" type, since that one's close-on-select/re-click behavior isn't // reliably patchable from a gadget. Reuses wikiEditor's own CSS classes so // it looks like a native dropdown. function buildCaseChangeElement() { var forms = [{ key: "upper", label: getMessage("caseUpperLabel") }, { key: "lower", label: getMessage("caseLowerLabel") }, { key: "sentence", label: getMessage("caseSentenceLabel") }, { key: "title", label: getMessage("caseTitleLabel") } ]; var $select = $("<div>").addClass("tool tool-select").attr("rel", "caseChange"); var $options = $("<div>").addClass("options"); var $menu = $("<div>").addClass("menu").append($options).hide(); function closeMenu() { $select.removeClass("options-shown"); $menu.hide(); $label.attr("aria-expanded", "false"); } function openMenu() { // Only one such dropdown open at a time. $(".tool-select").not($select).removeClass("options-shown").find(".menu").hide(); $select.addClass("options-shown"); $menu.show(); $label.attr("aria-expanded", "true"); } $.each(forms, function(i, form) { $("<a>") .addClass("option") .attr({ rel: form.key, tabindex: 0, role: "menuitem" }) .text(form.label) .on("mousedown", function(e) { e.preventDefault(); return false; }) .on("click keydown", function(e) { if (e.type === "click" || (e.type === "keydown" && e.key === "Enter")) { applyCaseToSelection(form.key); closeMenu(); e.preventDefault(); return false; } }) .appendTo($options); }); var $labelIcon = $("<img>") .addClass("oo-ui-image") .attr({ src: kuGetIcon("searchCaseSensitive"), alt: "" }); var $label = $("<a>") .addClass("label") .attr({ role: "button", tabindex: 0, "aria-expanded": "false", "aria-haspopup": "menu", title: getMessage("caseChangeLabel") }) .css({ display: "inline-flex", "align-items": "center", gap: "4px" }) .append($labelIcon, $("<span>").text("Mezin/biçûk")) .on("mousedown", function(e) { e.preventDefault(); return false; }) .on("click keydown", function(e) { if (e.type === "click" || (e.type === "keydown" && e.key === "Enter")) { if ($select.hasClass("options-shown")) { closeMenu(); } else { openMenu(); } e.preventDefault(); return false; } }); $select.append($label, $menu); // Close on any click elsewhere. Re-bound via .off() first so // re-registering this tool never stacks up duplicate handlers. $(document).off("mousedown.kuCaseChange").on("mousedown.kuCaseChange", function(e) { if (!$(e.target).closest($select).length) { closeMenu(); } }); return $select; } function getCaseChangeTool() { return { type: "element", element: buildCaseChangeElement }; } function addLinkifyButton() { var $textbox = $("#wpTextbox1"); // No edit form on this page (VisualEditor, view/history page, etc.). if (!$textbox.length) return; if ($.kuLinkify) { $textbox.after($(mw.html.element("div", { id: "linkify-progress", title: getMessage("toolbarButtonName") })).hide()); } // Single source of truth so both registration paths below never // build duplicate tool objects. function buildTools() { return { linkify: getLinkifyTool(), delinkify: getDelinkifyTool(), caseChange: getCaseChangeTool() }; } // Guards against adding the tools twice: the buildSection event and // the mw.loader.using callback below can both fire on the same load. var toolsAdded = false; // Rebuilds the kutools toolbar group so its icons pick up whichever // light/dark icon variant matches the theme active right now -- // used when the day/night/os toggle or the OS-level color scheme // changes without a page reload. function rebuildIcons() { var context = $textbox.data("wikiEditor-context"); if (!context || !context.modules || !context.modules.toolbar) return; $textbox.wikiEditor("removeFromToolbar", { section: "main", group: "kutools" }); $textbox.wikiEditor("addToToolbar", { section: "main", groups: { kutools: { tools: buildTools() } } }); } // Bind synchronously (not inside mw.loader.using's callback) so we // don't miss the event if ext.wikiEditor resolves instantly/cached. $textbox.on('wikiEditor-toolbar-buildSection-main', function(evt, section) { section.groups.kutools = { tools: buildTools() }; toolsAdded = true; }); mw.loader.using("ext.wikiEditor", function () { if (toolsAdded) return; // Fallback: toolbar was already built (and the event already fired) // before this script ran -- add tools directly via the public API. var context = $textbox.data("wikiEditor-context"); if (context && context.modules && context.modules.toolbar) { $textbox.wikiEditor("addToToolbar", { section: "main", groups: { kutools: { tools: buildTools() } } }); toolsAdded = true; } }); // Watch for the OS-level color scheme changing (system dark mode // toggled while the page is open). if (window.matchMedia) { var kuDarkModeQuery = window.matchMedia("(prefers-color-scheme: dark)"); var kuOnSchemeChange = function() { if (!toolsAdded) return; rebuildIcons(); }; if (kuDarkModeQuery.addEventListener) { kuDarkModeQuery.addEventListener("change", kuOnSchemeChange); } else if (kuDarkModeQuery.addListener) { // Older browsers. kuDarkModeQuery.addListener(kuOnSchemeChange); } } // Watch for Vector 2022's day/night/os toggle, which flips a class // on <html> without reloading the page. if (window.MutationObserver) { var kuThemeObserver = new MutationObserver(function() { if (!toolsAdded) return; rebuildIcons(); }); kuThemeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] }); } } // Only run on edit pages -- avoids a wasted mw.loader.using round trip // elsewhere, where #wpTextbox1 never exists anyway. if ($.inArray(mw.config.get("wgAction"), ["edit", "submit"]) !== -1) { addLinkifyButton(); } })(jQuery); 6719abrhn69xqi4pj1a8u5y4wrbxzn4 Bikarhêner:Wikihez/Gadget-start.js 2 974484 6291577 6291556 2026-07-26T17:08:30Z Wikihez 7344 6291577 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; function getZaravaKeys(choices) { var byCode = {}; $.each(choices || [], function (_, c) { byCode[c[0]] = 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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 (_, 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(tpl.name + " — " + tpl.desc) .attr("data-nolang", tpl.noLang ? "1" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var lang = getValues().lang || ""; $.each(steps.pron.pronTpls, function (_, 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(tpl.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [[code, genderLabel(code)]]; }); populateMenus(select, choices); if ($.grep(choices, function (c) { return c[0] === prevVal; }).length) { select.val(prevVal); } if (!isMobile) select.trigger("chosen:updated"); return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. choices: [ ["gh", "gerguhêz"], ['x-gh', 'xwerû-gerguhêz'], ['p-gh', 'pêkhatî-gerguhêz'], ['h-gh', 'hevedudanî-gerguhêz'], ['b-gh', 'biwêjî-gerguhêz'], ["ngh", "negerguhêz"], ['x-ngh', 'xwerû-negerguhêz'], ['p-ngh', 'pêkhatî-negerguhêz'], ['h-ngh', 'hevedudanî-negerguhêz'], ['b-ngh', 'biwêjî-negerguhêz'] ], output: function () { return ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (_, tpl) { var opt = $("<option></option>") .val(tpl.name) .text(tpl.name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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"); srcSel.trigger("chosen:updated"); } 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"); 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 (_, f) { var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row"); var fieldLbl = $("<label></label>").text(f.label + ": "); var inp = $("<textarea></textarea>") .attr("rows", 2) .addClass("wiktku-dfn-quote-src-input wiktku-start-narrow") .attr("data-field-key", f.key) .attr("placeholder", f.label + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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 array. 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 (_, tpl) { sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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", "Nimûne: 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); $.each(steps.etym.etymTpls, function (_, tpl) { sel.append($("<option></option>").val(tpl.snippet).text(tpl.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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" })); 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); } 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); }); }); }, /** 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); 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 ""; 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) return undefined; if (rows.length) { rows.sort(function (a, b) { var aLang = langsByCode[a[0]] || []; var bLang = langsByCode[b[0]] || []; return (aLang[2] || "").localeCompare(bLang[2] || "", "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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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", 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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[2] || "").localeCompare(bLang[2] || "", "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 ên (ne jêgirtinan) 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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 jêrê 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var status = $("<span class='wiktku-cavkani-status'></span>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); if (!isMobile) sel.trigger("chosen:updated"); ta.focus(); }); wrap.append(sel).append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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) { if (!disabledValues) disabledValues = {}; var vals = inputs.map(function (i, input) { return $(input).val(); }); inputs.empty(); inputs.append($("<option></option>")); $.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[1]); if (choice[2] !== undefined) { // This is a language choice [code, name, sortKey] — show code. displayText = capitalize(choice[1]) + " (" + choice[0] + ")"; } var option = $(mw.html.element("option", { value: choice[0] }, displayText)); if (disabledValues[choice[0]]) 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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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[1] : 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke", framed: false }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").chosen(chosenOptions); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice[0]; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = [code, name, sortKey]; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey (index 2). if (choice.length < 3) choice[2] = choice[1]; langsByCode[choice[0]] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a[2].localeCompare(b[2], userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava)); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — this block only needs to wait // on the UI modules itself. mw.loader.using(["jquery.chosen", "oojs-ui-core"], function () { if (!isMobile) { mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); } // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). if (d.langChoices) steps.lang.choices = d.langChoices; // Populate POS choices. if (d.posChoices) steps.pos.choices = d.posChoices; // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> 2dpgy94jef0yyeungjul1j7njo4ndty 6291578 6291577 2026-07-26T17:18:33Z Wikihez 7344 6291578 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. // Computed early (no async dependency) so the "starting up" notice below can // fire immediately, rather than waiting on any module load. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; if (!isMobile) { mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); } // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; function getZaravaKeys(choices) { var byCode = {}; $.each(choices || [], function (_, c) { byCode[c[0]] = 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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 (_, 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(tpl.name + " — " + tpl.desc) .attr("data-nolang", tpl.noLang ? "1" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var lang = getValues().lang || ""; $.each(steps.pron.pronTpls, function (_, 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(tpl.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [[code, genderLabel(code)]]; }); populateMenus(select, choices); if ($.grep(choices, function (c) { return c[0] === prevVal; }).length) { select.val(prevVal); } if (!isMobile) select.trigger("chosen:updated"); return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. choices: [ ["gh", "gerguhêz"], ['x-gh', 'xwerû-gerguhêz'], ['p-gh', 'pêkhatî-gerguhêz'], ['h-gh', 'hevedudanî-gerguhêz'], ['b-gh', 'biwêjî-gerguhêz'], ["ngh", "negerguhêz"], ['x-ngh', 'xwerû-negerguhêz'], ['p-ngh', 'pêkhatî-negerguhêz'], ['h-ngh', 'hevedudanî-negerguhêz'], ['b-ngh', 'biwêjî-negerguhêz'] ], output: function () { return ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (_, tpl) { var opt = $("<option></option>") .val(tpl.name) .text(tpl.name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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"); srcSel.trigger("chosen:updated"); } 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"); 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 (_, f) { var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row"); var fieldLbl = $("<label></label>").text(f.label + ": "); var inp = $("<textarea></textarea>") .attr("rows", 2) .addClass("wiktku-dfn-quote-src-input wiktku-start-narrow") .attr("data-field-key", f.key) .attr("placeholder", f.label + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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 array. 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 (_, tpl) { sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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", "Nimûne: 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); $.each(steps.etym.etymTpls, function (_, tpl) { sel.append($("<option></option>").val(tpl.snippet).text(tpl.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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" })); 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); } 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); }); }); }, /** 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); 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 ""; 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) return undefined; if (rows.length) { rows.sort(function (a, b) { var aLang = langsByCode[a[0]] || []; var bLang = langsByCode[b[0]] || []; return (aLang[2] || "").localeCompare(bLang[2] || "", "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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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", 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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[2] || "").localeCompare(bLang[2] || "", "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 ên (ne jêgirtinan) 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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 jêrê 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var status = $("<span class='wiktku-cavkani-status'></span>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); if (!isMobile) sel.trigger("chosen:updated"); ta.focus(); }); wrap.append(sel).append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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) { if (!disabledValues) disabledValues = {}; var vals = inputs.map(function (i, input) { return $(input).val(); }); inputs.empty(); inputs.append($("<option></option>")); $.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[1]); if (choice[2] !== undefined) { // This is a language choice [code, name, sortKey] — show code. displayText = capitalize(choice[1]) + " (" + choice[0] + ")"; } var option = $(mw.html.element("option", { value: choice[0] }, displayText)); if (disabledValues[choice[0]]) 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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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[1] : 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke", framed: false }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").chosen(chosenOptions); refresh(); // The language list (pinned + full ziman.json list, merged) is already // finalized in steps.lang.choices/steps.trans.keys/etc. before build() // runs, so every select above was already created with its full option // list — no repopulation needed here. // Silently restore any draft the editor previously saved for this page // (if the "Li cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — nothing here depends on them // finishing first. var uiModulesPromise = mw.loader.using(["jquery.chosen", "oojs-ui-core"]); // Process data.json: metadata, gender labels, POS choices, template defs, // and the pinned language list (steps.lang.choices). var dataProcessedPromise = metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } if (d.metadata) $.extend(metadata, d.metadata); if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Pinned list, used below to merge with the full ziman.json list. if (d.langChoices) steps.lang.choices = d.langChoices; if (d.posChoices) steps.pos.choices = d.posChoices; if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs; if (d.posExamples) steps.dfn.posExamples = d.posExamples; if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs; if (d.pronTpls) { steps.pron.pronTpls = d.pronTpls; steps.pron.refreshTplSelect(); } if (d.etymTpls) { steps.etym.etymTpls = d.etymTpls; steps.etym.refreshTplSelect(); } }); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Chained after dataProcessedPromise (needs the pinned list above to know // which codes to skip) and merged into steps.*.choices/keys before build() // ever runs, so every select is created with its full, final option list — // no separate repopulation step once the form is already on screen. var langsReadyPromise = dataProcessedPromise.then(function () { return zimanJsonPromise; }).then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice[0]; }); var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = [code, name, sortKey]; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey (index 2). if (choice.length < 3) choice[2] = choice[1]; langsByCode[choice[0]] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a[2].localeCompare(b[2], userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); }); // Only build (and show) the form once the UI modules are loaded AND the // full, merged language list is ready — so the buttons and language // dropdowns appear complete on first paint instead of popping in a moment // after the rest of the form. $.when(uiModulesPromise, langsReadyPromise).always(build); }); //</nowiki> mssqyqxxdwz7t1vyma0jeznw20b9ic4 6291580 6291578 2026-07-26T17:27:48Z Wikihez 7344 6291580 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. // Computed early (no async dependency) so the "starting up" notice below can // fire immediately, rather than waiting on any module load. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; if (!isMobile) { mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); } // Kick off the ziman.json request as early as possible — the language // dropdown only needs this plus the in-script PINNED_LANG_CHOICES list // (defined below), not data.json. Needs the mediawiki.api module; that // module is used all over the wiki so this resolves near-instantly in // practice, but we still wait for it properly rather than assuming // mw.Api is already defined. data.json is fetched separately, only once // the form itself has been built (see loadDataJson() further down) — // nothing in the initial render needs it. var zimanJsonPromise; var mediawikiApiReadyPromise = mw.loader.using("mediawiki.api").then(function () { zimanJsonPromise = new mw.Api().get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return zimanJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Pinned languages, shown first in the "ziman" (language) dropdown ahead // of the full alphabetical list loaded from ziman.json. Kept in the // script (rather than data.json) since the language dropdown doesn't // need anything else from data.json to build. Order here is preserved // (insertion order of string keys), so keep the most relevant languages // first. var PINNED_LANG_CHOICES = { "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î" }; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; function getZaravaKeys(choices) { var byCode = {}; $.each(choices || [], function (_, c) { byCode[c[0]] = 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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 PINNED_LANG_CHOICES + ziman.json before build() runs. 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 (_, 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(tpl.name + " — " + tpl.desc) .attr("data-nolang", tpl.noLang ? "1" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var lang = getValues().lang || ""; $.each(steps.pron.pronTpls, function (_, 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(tpl.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [[code, genderLabel(code)]]; }); populateMenus(select, choices); if ($.grep(choices, function (c) { return c[0] === prevVal; }).length) { select.val(prevVal); } if (!isMobile) select.trigger("chosen:updated"); return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. choices: [ ["gh", "gerguhêz"], ['x-gh', 'xwerû-gerguhêz'], ['p-gh', 'pêkhatî-gerguhêz'], ['h-gh', 'hevedudanî-gerguhêz'], ['b-gh', 'biwêjî-gerguhêz'], ["ngh", "negerguhêz"], ['x-ngh', 'xwerû-negerguhêz'], ['p-ngh', 'pêkhatî-negerguhêz'], ['h-ngh', 'hevedudanî-negerguhêz'], ['b-ngh', 'biwêjî-negerguhêz'] ], output: function () { return ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (_, tpl) { var opt = $("<option></option>") .val(tpl.name) .text(tpl.name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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"); srcSel.trigger("chosen:updated"); } 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"); 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 (_, f) { var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row"); var fieldLbl = $("<label></label>").text(f.label + ": "); var inp = $("<textarea></textarea>") .attr("rows", 2) .addClass("wiktku-dfn-quote-src-input wiktku-start-narrow") .attr("data-field-key", f.key) .attr("placeholder", f.label + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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 array. 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 (_, tpl) { sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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", "Nimûne: 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); $.each(steps.etym.etymTpls, function (_, tpl) { sel.append($("<option></option>").val(tpl.snippet).text(tpl.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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" })); 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); } 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); }); }); }, /** 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); 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 ""; 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) return undefined; if (rows.length) { rows.sort(function (a, b) { var aLang = langsByCode[a[0]] || []; var bLang = langsByCode[b[0]] || []; return (aLang[2] || "").localeCompare(bLang[2] || "", "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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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", 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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[2] || "").localeCompare(bLang[2] || "", "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 ên (ne jêgirtinan) 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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 jêrê 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var status = $("<span class='wiktku-cavkani-status'></span>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); if (!isMobile) sel.trigger("chosen:updated"); ta.focus(); }); wrap.append(sel).append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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) { if (!disabledValues) disabledValues = {}; var vals = inputs.map(function (i, input) { return $(input).val(); }); inputs.empty(); inputs.append($("<option></option>")); $.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[1]); if (choice[2] !== undefined) { // This is a language choice [code, name, sortKey] — show code. displayText = capitalize(choice[1]) + " (" + choice[0] + ")"; } var option = $(mw.html.element("option", { value: choice[0] }, displayText)); if (disabledValues[choice[0]]) 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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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[1] : 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke", framed: false }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").chosen(chosenOptions); refresh(); // The language list (pinned + full ziman.json list, merged) is already // finalized in steps.lang.choices/steps.trans.keys/etc. before build() // runs, so every select above was already created with its full option // list — no repopulation needed here. // Silently restore any draft the editor previously saved for this page // (if the "Li cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } } // jquery.chosen/oojs-ui-core load in parallel with mediawikiApiReadyPromise // above (mediawiki.api + the ziman.json request) — nothing here depends on // them finishing first. var uiModulesPromise = mw.loader.using(["jquery.chosen", "oojs-ui-core"]); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike, // pê re bi PINNED_LANG_CHOICES re tevlihev bike. Kodên ku bi "-pro" diqedin // û navên ku "zimanê destan" tê de heye tên derxistin. Zimanên xardkirî li // serî dimînin, yên din bi alfabeyê li dû wan tên. // Merged into steps.*.choices/keys before build() ever runs, so every // select is created with its full, final option list — no separate // repopulation step once the form is already on screen. This doesn't // touch data.json at all, so it's ready as soon as ziman.json is. var langsReadyPromise = mediawikiApiReadyPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var pinnedChoices = []; $.each(PINNED_LANG_CHOICES, function (code, name) { pinnedChoices.push([code, name]); }); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice[0]; }); var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = [code, name, sortKey]; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey (index 2). if (choice.length < 3) choice[2] = choice[1]; langsByCode[choice[0]] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a[2].localeCompare(b[2], userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); }); // Fetches data.json (metadata, gender labels, POS choices, template defs) // and applies it. Called only once the form is already built, since none // of this is needed for the initial render — the pos/pron/etym/dfn steps // that depend on it are only built lazily, once the editor reaches them // (see refresh()'s step.visible() gate), by which point this will // ordinarily have already resolved. function loadDataJson() { new mw.Api().get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }).then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } if (d.metadata) $.extend(metadata, d.metadata); if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); if (d.posChoices) steps.pos.choices = d.posChoices; if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs; if (d.posExamples) steps.dfn.posExamples = d.posExamples; if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs; if (d.pronTpls) { steps.pron.pronTpls = d.pronTpls; steps.pron.refreshTplSelect(); } if (d.etymTpls) { steps.etym.etymTpls = d.etymTpls; steps.etym.refreshTplSelect(); } }); } // Only build (and show) the form once the UI modules are loaded AND the // full, merged language list is ready — so the buttons and language // dropdown appear complete on first paint instead of popping in a moment // after the rest of the form. data.json loads afterward, in the background. $.when(uiModulesPromise, langsReadyPromise).always(function () { build(); loadDataJson(); }); }); //</nowiki> r9fngi12l6cukq7jpctixfmmp8vhgkv 6291581 6291580 2026-07-26T17:29:22Z Wikihez 7344 Restored revision 6291577 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]]) 6291581 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; function getZaravaKeys(choices) { var byCode = {}; $.each(choices || [], function (_, c) { byCode[c[0]] = 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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 (_, 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(tpl.name + " — " + tpl.desc) .attr("data-nolang", tpl.noLang ? "1" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var lang = getValues().lang || ""; $.each(steps.pron.pronTpls, function (_, 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(tpl.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [[code, genderLabel(code)]]; }); populateMenus(select, choices); if ($.grep(choices, function (c) { return c[0] === prevVal; }).length) { select.val(prevVal); } if (!isMobile) select.trigger("chosen:updated"); return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. choices: [ ["gh", "gerguhêz"], ['x-gh', 'xwerû-gerguhêz'], ['p-gh', 'pêkhatî-gerguhêz'], ['h-gh', 'hevedudanî-gerguhêz'], ['b-gh', 'biwêjî-gerguhêz'], ["ngh", "negerguhêz"], ['x-ngh', 'xwerû-negerguhêz'], ['p-ngh', 'pêkhatî-negerguhêz'], ['h-ngh', 'hevedudanî-negerguhêz'], ['b-ngh', 'biwêjî-negerguhêz'] ], output: function () { return ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (_, tpl) { var opt = $("<option></option>") .val(tpl.name) .text(tpl.name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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"); srcSel.trigger("chosen:updated"); } 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"); 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 (_, f) { var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row"); var fieldLbl = $("<label></label>").text(f.label + ": "); var inp = $("<textarea></textarea>") .attr("rows", 2) .addClass("wiktku-dfn-quote-src-input wiktku-start-narrow") .attr("data-field-key", f.key) .attr("placeholder", f.label + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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 array. 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 (_, tpl) { sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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", "Nimûne: 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); $.each(steps.etym.etymTpls, function (_, tpl) { sel.append($("<option></option>").val(tpl.snippet).text(tpl.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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" })); 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); } 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); }); }); }, /** 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); 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 ""; 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) return undefined; if (rows.length) { rows.sort(function (a, b) { var aLang = langsByCode[a[0]] || []; var bLang = langsByCode[b[0]] || []; return (aLang[2] || "").localeCompare(bLang[2] || "", "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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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", 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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[2] || "").localeCompare(bLang[2] || "", "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 ên (ne jêgirtinan) 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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 jêrê 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var status = $("<span class='wiktku-cavkani-status'></span>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); if (!isMobile) sel.trigger("chosen:updated"); ta.focus(); }); wrap.append(sel).append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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) { if (!disabledValues) disabledValues = {}; var vals = inputs.map(function (i, input) { return $(input).val(); }); inputs.empty(); inputs.append($("<option></option>")); $.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[1]); if (choice[2] !== undefined) { // This is a language choice [code, name, sortKey] — show code. displayText = capitalize(choice[1]) + " (" + choice[0] + ")"; } var option = $(mw.html.element("option", { value: choice[0] }, displayText)); if (disabledValues[choice[0]]) 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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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[1] : 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke", framed: false }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").chosen(chosenOptions); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice[0]; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = [code, name, sortKey]; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey (index 2). if (choice.length < 3) choice[2] = choice[1]; langsByCode[choice[0]] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a[2].localeCompare(b[2], userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava)); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — this block only needs to wait // on the UI modules itself. mw.loader.using(["jquery.chosen", "oojs-ui-core"], function () { if (!isMobile) { mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); } // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). if (d.langChoices) steps.lang.choices = d.langChoices; // Populate POS choices. if (d.posChoices) steps.pos.choices = d.posChoices; // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> 2dpgy94jef0yyeungjul1j7njo4ndty 6291582 6291581 2026-07-26T17:30:27Z Wikihez 7344 6291582 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; function getZaravaKeys(choices) { var byCode = {}; $.each(choices || [], function (_, c) { byCode[c[0]] = 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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 (_, 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(tpl.name + " — " + tpl.desc) .attr("data-nolang", tpl.noLang ? "1" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var lang = getValues().lang || ""; $.each(steps.pron.pronTpls, function (_, 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(tpl.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [[code, genderLabel(code)]]; }); populateMenus(select, choices); if ($.grep(choices, function (c) { return c[0] === prevVal; }).length) { select.val(prevVal); } if (!isMobile) select.trigger("chosen:updated"); return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. choices: [ ["gh", "gerguhêz"], ['x-gh', 'xwerû-gerguhêz'], ['p-gh', 'pêkhatî-gerguhêz'], ['h-gh', 'hevedudanî-gerguhêz'], ['b-gh', 'biwêjî-gerguhêz'], ["ngh", "negerguhêz"], ['x-ngh', 'xwerû-negerguhêz'], ['p-ngh', 'pêkhatî-negerguhêz'], ['h-ngh', 'hevedudanî-negerguhêz'], ['b-ngh', 'biwêjî-negerguhêz'] ], output: function () { return ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (_, tpl) { var opt = $("<option></option>") .val(tpl.name) .text(tpl.name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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"); srcSel.trigger("chosen:updated"); } 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"); 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 (_, f) { var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row"); var fieldLbl = $("<label></label>").text(f.label + ": "); var inp = $("<textarea></textarea>") .attr("rows", 2) .addClass("wiktku-dfn-quote-src-input wiktku-start-narrow") .attr("data-field-key", f.key) .attr("placeholder", f.label + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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 array. 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 (_, tpl) { sel.append($("<option></option>").val(tpl.snippet).text(tpl.name + " — " + tpl.desc)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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", "Nimûne: 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); $.each(steps.etym.etymTpls, function (_, tpl) { sel.append($("<option></option>").val(tpl.snippet).text(tpl.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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" })); 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); } 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); }); }); }, /** 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); 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 ""; 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) return undefined; if (rows.length) { rows.sort(function (a, b) { var aLang = langsByCode[a[0]] || []; var bLang = langsByCode[b[0]] || []; return (aLang[2] || "").localeCompare(bLang[2] || "", "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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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", 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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[2] || "").localeCompare(bLang[2] || "", "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 ên (ne jêgirtinan) 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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 jêrê 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var status = $("<span class='wiktku-cavkani-status'></span>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); if (!isMobile) sel.trigger("chosen:updated"); ta.focus(); }); wrap.append(sel).append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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) { if (!disabledValues) disabledValues = {}; var vals = inputs.map(function (i, input) { return $(input).val(); }); inputs.empty(); inputs.append($("<option></option>")); $.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[1]); if (choice[2] !== undefined) { // This is a language choice [code, name, sortKey] — show code. displayText = capitalize(choice[1]) + " (" + choice[0] + ")"; } var option = $(mw.html.element("option", { value: choice[0] }, displayText)); if (disabledValues[choice[0]]) 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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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[1] : 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke", framed: false }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").chosen(chosenOptions); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice[0]; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = [code, name, sortKey]; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey (index 2). if (choice.length < 3) choice[2] = choice[1]; langsByCode[choice[0]] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a[2].localeCompare(b[2], userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava)); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — this block only needs to wait // on the UI modules itself. mw.loader.using(["jquery.chosen", "oojs-ui-core"], function () { mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). if (d.langChoices) steps.lang.choices = d.langChoices; // Populate POS choices. if (d.posChoices) steps.pos.choices = d.posChoices; // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> q75ymz0jfogy00s6x5vit4wjf9kb0en 6291587 6291582 2026-07-26T18:05:33Z Wikihez 7344 6291587 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; // Converts a plain {code: label} object (the format used for langChoices // and posChoices in data.json) into the ordered array of choice objects // ({code, label[, sortKey]}) that the rest of this script works with. // isLang: true adds a sortKey (defaults to label) and tells populateMenus // to show "(code)" after the label — used for 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [{ code: code, label: genderLabel(code) }]; }); populateMenus(select, choices); if ($.grep(choices, function (c) { return c.code === prevVal; }).length) { select.val(prevVal); } if (!isMobile) select.trigger("chosen:updated"); return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. choices: [ ["gh", "gerguhêz"], ['x-gh', 'xwerû-gerguhêz'], ['p-gh', 'pêkhatî-gerguhêz'], ['h-gh', 'hevedudanî-gerguhêz'], ['b-gh', 'biwêjî-gerguhêz'], ["ngh", "negerguhêz"], ['x-ngh', 'xwerû-negerguhêz'], ['p-ngh', 'pêkhatî-negerguhêz'], ['h-ngh', 'hevedudanî-negerguhêz'], ['b-ngh', 'biwêjî-negerguhêz'] ], output: function () { return ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (name, tpl) { var opt = $("<option></option>") .val(name) .text(name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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"); srcSel.trigger("chosen:updated"); } 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"); 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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", "Nimûne: 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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" })); 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); } 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); }); }); }, /** 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); 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 ""; 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) 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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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", 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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 ên (ne jêgirtinan) 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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 jêrê 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var status = $("<span class='wiktku-cavkani-status'></span>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); if (!isMobile) sel.trigger("chosen:updated"); ta.focus(); }); wrap.append(sel).append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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) { if (!disabledValues) disabledValues = {}; var vals = inputs.map(function (i, input) { return $(input).val(); }); inputs.empty(); inputs.append($("<option></option>")); $.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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke", framed: false }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").chosen(chosenOptions); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice.code; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = { code: code, label: name, sortKey: sortKey }; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey. if (choice.sortKey === undefined) choice.sortKey = choice.label; langsByCode[choice.code] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a.sortKey.localeCompare(b.sortKey, userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava)); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — this block only needs to wait // on the UI modules itself. mw.loader.using(["jquery.chosen", "oojs-ui-core"], function () { mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). // data.json stores these as a plain {code: label} object. if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true); // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> ij43mnwfsxa2sfqfodaix0omguumzlc 6291588 6291587 2026-07-26T18:07:50Z Wikihez 7344 6291588 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; // Converts a plain {code: label} object (the format used for langChoices // and posChoices in data.json) into the ordered array of choice objects // ({code, label[, sortKey]}) that the rest of this script works with. // isLang: true adds a sortKey (defaults to label) and tells populateMenus // to show "(code)" after the label — used for 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [{ code: code, label: genderLabel(code) }]; }); populateMenus(select, choices); if ($.grep(choices, function (c) { return c.code === prevVal; }).length) { select.val(prevVal); } if (!isMobile) select.trigger("chosen:updated"); return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. choices: [ ["gh", "gerguhêz"], ['x-gh', 'xwerû-gerguhêz'], ['p-gh', 'pêkhatî-gerguhêz'], ['h-gh', 'hevedudanî-gerguhêz'], ['b-gh', 'biwêjî-gerguhêz'], ["ngh", "negerguhêz"], ['x-ngh', 'xwerû-negerguhêz'], ['p-ngh', 'pêkhatî-negerguhêz'], ['h-ngh', 'hevedudanî-negerguhêz'], ['b-ngh', 'biwêjî-negerguhêz'] ], output: function () { return ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (name, tpl) { var opt = $("<option></option>") .val(name) .text(name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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"); srcSel.trigger("chosen:updated"); } 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"); 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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", "Nimûne: 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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" })); 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); } 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); }); }); }, /** 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); 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 ""; 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) 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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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", 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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 ên (ne jêgirtinan) 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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 jêrê 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var status = $("<span class='wiktku-cavkani-status'></span>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); if (!isMobile) sel.trigger("chosen:updated"); ta.focus(); }); wrap.append(sel).append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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) { if (!disabledValues) disabledValues = {}; var vals = inputs.map(function (i, input) { return $(input).val(); }); inputs.empty(); inputs.append($("<option></option>")); $.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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke", framed: false }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").chosen(chosenOptions); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice.code; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = { code: code, label: name, sortKey: sortKey }; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey. if (choice.sortKey === undefined) choice.sortKey = choice.label; langsByCode[choice.code] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a.sortKey.localeCompare(b.sortKey, userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava)); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — this block only needs to wait // on the UI modules itself. mw.loader.using(["jquery.chosen", "oojs-ui-core"], function () { // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). // data.json stores these as a plain {code: label} object. if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true); // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> 8unyik6xvvsghe5h9oumevrqsr53269 6291590 6291588 2026-07-26T18:41:25Z Wikihez 7344 6291590 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; // Converts a plain {code: label} object (the format used for langChoices // and posChoices in data.json) into the ordered array of choice objects // ({code, label[, sortKey]}) that the rest of this script works with. // isLang: true adds a sortKey (defaults to label) and tells populateMenus // to show "(code)" after the label — used for 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [{ code: code, label: genderLabel(code) }]; }); populateMenus(select, choices); if ($.grep(choices, function (c) { return c.code === prevVal; }).length) { select.val(prevVal); } if (!isMobile) select.trigger("chosen:updated"); return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. choices: [ ["gh", "gerguhêz"], ['x-gh', 'xwerû-gerguhêz'], ['p-gh', 'pêkhatî-gerguhêz'], ['h-gh', 'hevedudanî-gerguhêz'], ['b-gh', 'biwêjî-gerguhêz'], ["ngh", "negerguhêz"], ['x-ngh', 'xwerû-negerguhêz'], ['p-ngh', 'pêkhatî-negerguhêz'], ['h-ngh', 'hevedudanî-negerguhêz'], ['b-ngh', 'biwêjî-negerguhêz'] ], output: function () { return ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (name, tpl) { var opt = $("<option></option>") .val(name) .text(name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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"); srcSel.trigger("chosen:updated"); } 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"); 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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", "Nimûne: 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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" })); 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); } 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); }); }); }, /** 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); 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 ""; 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) 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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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", 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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 ên (ne jêgirtinan) 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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 jêrê 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var status = $("<span class='wiktku-cavkani-status'></span>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); if (!isMobile) sel.trigger("chosen:updated"); ta.focus(); }); wrap.append(sel).append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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) { if (!disabledValues) disabledValues = {}; var vals = inputs.map(function (i, input) { return $(input).val(); }); inputs.empty(); inputs.append($("<option></option>")); $.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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke", framed: false }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").chosen(chosenOptions); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice.code; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = { code: code, label: name, sortKey: sortKey }; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey. if (choice.sortKey === undefined) choice.sortKey = choice.label; langsByCode[choice.code] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a.sortKey.localeCompare(b.sortKey, userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava)); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — this block only needs to wait // on the UI modules itself. mw.loader.using(["jquery.chosen", "oojs-ui-core"], function () { mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). // data.json stores these as a plain {code: label} object. if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true); // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> ij43mnwfsxa2sfqfodaix0omguumzlc 6291591 6291590 2026-07-26T18:41:37Z Wikihez 7344 Restored revision 6291588 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]]) 6291591 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; // Converts a plain {code: label} object (the format used for langChoices // and posChoices in data.json) into the ordered array of choice objects // ({code, label[, sortKey]}) that the rest of this script works with. // isLang: true adds a sortKey (defaults to label) and tells populateMenus // to show "(code)" after the label — used for 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [{ code: code, label: genderLabel(code) }]; }); populateMenus(select, choices); if ($.grep(choices, function (c) { return c.code === prevVal; }).length) { select.val(prevVal); } if (!isMobile) select.trigger("chosen:updated"); return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. choices: [ ["gh", "gerguhêz"], ['x-gh', 'xwerû-gerguhêz'], ['p-gh', 'pêkhatî-gerguhêz'], ['h-gh', 'hevedudanî-gerguhêz'], ['b-gh', 'biwêjî-gerguhêz'], ["ngh", "negerguhêz"], ['x-ngh', 'xwerû-negerguhêz'], ['p-ngh', 'pêkhatî-negerguhêz'], ['h-ngh', 'hevedudanî-negerguhêz'], ['b-ngh', 'biwêjî-negerguhêz'] ], output: function () { return ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (name, tpl) { var opt = $("<option></option>") .val(name) .text(name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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"); srcSel.trigger("chosen:updated"); } 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"); 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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", "Nimûne: 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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" })); 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); } 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); }); }); }, /** 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); 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 ""; 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) 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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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", 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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 ên (ne jêgirtinan) 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)); }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, 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 jêrê 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.append($("<option value=''>— şablonek lê zêde bike —</option>")); var status = $("<span class='wiktku-cavkani-status'></span>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); if (!isMobile) sel.trigger("chosen:updated"); ta.focus(); }); wrap.append(sel).append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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) { if (!disabledValues) disabledValues = {}; var vals = inputs.map(function (i, input) { return $(input).val(); }); inputs.empty(); inputs.append($("<option></option>")); $.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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke", framed: false }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").chosen(chosenOptions); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice.code; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = { code: code, label: name, sortKey: sortKey }; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey. if (choice.sortKey === undefined) choice.sortKey = choice.label; langsByCode[choice.code] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a.sortKey.localeCompare(b.sortKey, userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava)); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — this block only needs to wait // on the UI modules itself. mw.loader.using(["jquery.chosen", "oojs-ui-core"], function () { // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). // data.json stores these as a plain {code: label} object. if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true); // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> 8unyik6xvvsghe5h9oumevrqsr53269 6291592 6291591 2026-07-26T19:00:52Z Wikihez 7344 6291592 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync; // otherwise (e.g. a "pick to insert" dropdown resetting itself // after use) clear the search box back to blank/placeholder. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } else { textInput.val(""); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; // Converts a plain {code: label} object (the format used for langChoices // and posChoices in data.json) into the ordered array of choice objects // ({code, label[, sortKey]}) that the rest of this script works with. // isLang: true adds a sortKey (defaults to label) and tells populateMenus // to show "(code)" after the label — used for 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field wiktku-plain-select", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [{ code: code, label: genderLabel(code) }]; }); populateMenus(select, choices, null, "Cinsiyetê hilbijêre"); if ($.grep(choices, function (c) { return c.code === prevVal; }).length) { select.val(prevVal); } return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. choices: [ ["gh", "gerguhêz"], ['x-gh', 'xwerû-gerguhêz'], ['p-gh', 'pêkhatî-gerguhêz'], ['h-gh', 'hevedudanî-gerguhêz'], ['b-gh', 'biwêjî-gerguhêz'], ["ngh", "negerguhêz"], ['x-ngh', 'xwerû-negerguhêz'], ['p-ngh', 'pêkhatî-negerguhêz'], ['h-ngh', 'hevedudanî-negerguhêz'], ['b-ngh', 'biwêjî-negerguhêz'] ], output: function () { return ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (name, tpl) { var opt = $("<option></option>") .val(name) .text(name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has changed. sel.trigger("chosen:updated"); }, 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", "Nimûne: 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("chosen:updated"); ta.focus(); }); if (isMobile) { wrap.append(wrapNativeSelect(sel)); } else { wrap.append(sel); } wrap.append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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 wiktku-plain-select" })); populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys, "Zarava hilbijêre"); 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); } 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); }); }); }, /** 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); 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 ""; 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) 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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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", 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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 ên (ne jêgirtinan) 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has changed. sel.trigger("chosen:updated"); }, 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 jêrê 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>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); sel.trigger("chosen:updated"); ta.focus(); }); if (isMobile) { wrap.append(wrapNativeSelect(sel)); } else { wrap.append(sel); } wrap.append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke" }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").not(".wiktku-plain-select").chosen(chosenOptions); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice.code; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = { code: code, label: name, sortKey: sortKey }; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey. if (choice.sortKey === undefined) choice.sortKey = choice.label; langsByCode[choice.code] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a.sortKey.localeCompare(b.sortKey, userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); if (!isMobile) langMenu.chosen("destroy").chosen(chosenOptions); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); if (!isMobile) transMenus.chosen("destroy").chosen(chosenOptions); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); if (!isMobile) dundeMenus.chosen("destroy").chosen(chosenOptions); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava), "Zarava hilbijêre"); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — this block only needs to wait // on the UI modules itself. mw.loader.using(["jquery.chosen", "oojs-ui-core"], function () { // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). // data.json stores these as a plain {code: label} object. if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true); // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> 50o6vcgdtskh6qgub3b0cintshz549a 6291593 6291592 2026-07-26T19:12:25Z Wikihez 7344 6291593 javascript text/javascript $(function(){if(window.location.href.includes("editintro=User:Wikihez/creation/intro")){return}var ns=mw.config.get("wgNamespaceNumber");if(ns&&!(ns===2&&mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))){return}var action=mw.config.get("wgAction");var pageExists=!!mw.config.get("wgArticleId");var addLangMode=pageExists&&mw.util.getParamValue("wiktkuaddlang")==="1";if(action==="view"&&pageExists){var targetContainer;var skin=mw.config.get("skin");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","Ji vê rûpelê re ziman an jî beşeke nû zêde bike");return}if(!(action==="edit"||action==="submit")){return}if(!addLangMode&&!$(".mw-newarticletext, .mw-newarticletextanon").length){return}mw.notify("Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin",{type:"info",tag:"wiktku-start-notice",autoHide:true,autoHideSeconds:10});var dataJsonPromise,zimanJsonPromise;var metaApiPromise=mw.loader.using("mediawiki.api").then(function(){var metaApi=new mw.Api;dataJsonPromise=metaApi.get({action:"query",prop:"revisions",titles:"MediaWiki:Gadget-start.js/data.json",rvprop:"content",rvslots:"main",formatversion:2});zimanJsonPromise=metaApi.get({action:"query",prop:"revisions",titles:"MediaWiki:Gadget-translation_editor.js/ziman.json",rvprop:"content",rvslots:"main",formatversion:2});return dataJsonPromise});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 langHeaderSource(codePattern){return"==\\s*\\{\\{\\s*ziman\\s*\\|\\s*("+codePattern+")\\s*\\}\\}\\s*=="}function findLangHeader(text,code){var m=new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);return m?{index:m.index,match:m[0]}:null}function stripWikilinks(str){return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g,function(m,target,display){return display||target})}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 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){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 isMobile=!!mw.config.get("wgMFMode")||window.matchMedia("(pointer: coarse)").matches;var chosenOptions={width:"20em",disable_search_threshold:0,placeholder_text_single:"Yekî hilbijêre",placeholder_text_multiple:"Hilbijêre",no_results_text:"Nehat dîtin",enable_split_word_search:true,search_contains:true};function wrapNativeSelect(select){var placeholder=select.attr("data-placeholder")||"Lê bigere…";select.hide();var wrapper=$("<span class='wiktku-native-select-wrap'></span>");var textInput=$("<input type='search' autocomplete='off'>").attr("placeholder",placeholder).addClass("wiktku-start-narrow wiktku-ac-input");var resultsList=$("<ul class='wiktku-ac-results'></ul>").hide();var allOptions=[];function cacheOptions(){allOptions=[];select.find("option").each(function(){var v=$(this).val();if(!v)return;allOptions.push({value:v,text:$(this).text(),disabled:$(this).prop("disabled")})})}cacheOptions();function showResults(query){var q=query.trim().toLowerCase();resultsList.empty();var matched;if(q){matched=[];$.each(allOptions,function(_,o){if(o.disabled)return;var codeMatch=o.value.toLowerCase().indexOf(q)!==-1;var nameMatch=o.text.toLowerCase().indexOf(q)!==-1;if(codeMatch||nameMatch){matched.push({opt:o,codeMatch:codeMatch})}});matched.sort(function(a,b){var aCode=a.opt.value.toLowerCase();var bCode=b.opt.value.toLowerCase();var aPrefix=aCode.indexOf(q)===0;var bPrefix=bCode.indexOf(q)===0;if(aPrefix!==bPrefix)return aPrefix?-1:1;if(a.codeMatch!==b.codeMatch)return a.codeMatch?-1:1;return 0});matched=matched.map(function(item){return item.opt})}else{matched=allOptions.filter(function(o){return!o.disabled})}if(!matched.length){resultsList.hide();return}$.each(matched.slice(0,50),function(_,opt){var li=$("<li></li>").text(opt.text).attr("data-value",opt.value);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();selectOption(opt.value,opt.text)});li.on("mousedown",function(e){e.preventDefault();selectOption(opt.value,opt.text)});resultsList.append(li)});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));resultsList.css({"max-height":maxH+"px",top:spaceBelow>=spaceAbove?"100%":"auto",bottom:spaceBelow>=spaceAbove?"auto":"100%"});resultsList.show()}function selectOption(value,text){textInput.val(text);select.val(value);resultsList.hide();select.trigger("change")}textInput.on("input focus",function(){showResults($(this).val())});textInput.on("blur",function(){setTimeout(function(){resultsList.hide()},200)});select.on("chosen:updated",function(){cacheOptions();var cur=select.val();if(cur){var match=$.grep(allOptions,function(o){return o.value===cur})[0];if(match)textInput.val(match.text)}else{textInput.val("")}});select.chosen=function(){return select};wrapper.append(textInput).append(resultsList).append(select);return wrapper}var langsByCode={};var metadata={};var ZARAVA_LANG_CODES=["ckb","sdh","lki","hac","zza"];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"}var GENDER_BASE_LABELS={};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}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}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#* {{bênimûne|"+lang+"}}"}return $.map(nimunes,function(n){var text=typeof n==="object"?n.text:n;if(!text)return undefined;var bolded=text.replace(new RegExp("("+pageNameRe+"([^( |,|;|\\?|\\.|\\!)]+)?)","ig"),"'''$1'''");var tpl="\n#: {{nm|"+lang+"|"+bolded;if(typeof n==="object"){if(n.tr)tpl+="|tr="+n.tr;if(n.t)tpl+="|"+n.t}if(text.length<200&&lang!=="ku")tpl+="|rêz=1";tpl+="}}";return tpl}).join("")}function formatJeder(jederList,lang){if(!jederList||!jederList.length)return"";return $.map(jederList,function(jeder){if(!jeder||!jeder.quote)return undefined;if(jeder.jederTpl){var cite="\n#* {{"+jeder.jederTpl+"|"+lang;cite+="|jêgirtin="+jeder.quote;if(jeder.quoteTr)cite+="|tr="+jeder.quoteTr;if(jeder.quoteT)cite+="|tercime="+jeder.quoteT;$.each(jeder.jêderFields||{},function(key,val){if(val)cite+="|"+key+"="+val});cite+="}}";return cite}var out="\n#* {{jêder|"+lang+"|jêgirtin="+jeder.quote;if(jeder.quoteTr)out+="|tr="+jeder.quoteTr;if(jeder.quoteT)out+="|tercime="+jeder.quoteT;var srcParam=jeder.jederCatTpl?"{{"+jeder.jederCatTpl+"}}":jeder.jederManual?jeder.jederManual:"çavkanî?";out+=(srcParam.indexOf("=")!==-1?"|2=":"|")+srcParam;out+="}}";return out}).join("")}function senseHasJeder(sense){return!!(sense.jeder&&$.grep(sense.jeder,function(j){return j&&j.quote}).length>0)}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}function buildCite(sense,lang){return formatNimune(sense.nimune,lang,senseHasJeder(sense))+formatJeder(sense.jeder,lang)}function makeWordListStep(id,name,question_fmt,header,templateName){return{id:id,name:name,question_fmt:question_fmt,multiple:true,narrow:true,visible:function(values){return values.lang&&values.pos},output:function(values){var words=$.grep(values[id]||[],function(w){return!!w});if(!words.length)return"";var lang=values.lang;var section="\n==== "+header+" ====\n";if(words.length===1){return section+"* {{"+templateName+"|"+lang+"|"+words[0]+"}}\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,choices:[],output:function(values){return"== {{ziman|"+values.lang+"}} ==\n"}},pron:{id:"pron",name:"bilêvkirin",question_fmt:'"'+pageName+'" çawa tê telafûzkirin?',pronTpls:{},visible:function(values){if(!values.lang)return false;if(addLangMode){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},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){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":""))}});if(!isMobile)sel.trigger("chosen:updated")},build:function(stepDiv){if(stepDiv.find(".wiktku-pron-wrap").length)return null;var wrap=$("<div class='wiktku-pron-wrap'></div>");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}}");var sel=$("<select class='wiktku-tpl-insert-select'></select>");sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));var lang=getValues().lang||"";$.each(steps.pron.pronTpls,function(name,tpl){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||"";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("");ta.focus()});wrap.append(sel).append($("<br>")).append(ta);stepDiv.append(wrap);return ta},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.charAt(0)==="*"?line:"* "+line}).join("\n");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,choices:[],output:function(values){var pos=values.pos||"";var cinsiyet=values.cinsiyet||"";var lekTur=values.lektur||"";var capitalizedPos=capitalize(pos);var outputString="\n=== "+capitalizedPos+" ===\n";if(!pageName.includes(" ")&&cinsiyet&&GENDER_BASE_LABELS[cinsiyet]&&(pos==="navdêr"||pos==="serenav")&&values.lang==="ku"){outputString+="{{ku-tewîn-nav|"+pageName+"|"+GENDER_BASE_LABELS[cinsiyet];if(pos==="serenav")outputString+="|y";outputString+="}}\n"}outputString+="{{"+pos+"|"+values.lang;if(pos==="navdêr"||pos==="serenav"){if(cinsiyet){outputString+="|c="+cinsiyet}else if(values.lang==="ku"){outputString+="|c=?"}}else if(/^lêker/.test(pos)&&lekTur){outputString+="|c="+lekTur}if(values.tr){outputString+="|tr="+values.tr}outputString+="}}\n";return outputString}},cinsiyet:{id:"cinsiyet",name:"cinsiyet",question_fmt:'Cinsiyeta "'+pageName+'" çi ye?',question_br:true,visible:function(values){var meta=metadata[values.lang];return!!(values.lang&&(values.pos==="navdêr"||values.pos==="serenav")&&meta&&meta.g&&meta.g.length)},build:function(stepDiv,values){var select=stepDiv.find("select.wiktku-start-step-field");if(!select.length){select=$(mw.html.element("select",{"class":"wiktku-start-step-field wiktku-plain-select",id:"wiktku-start-cinsiyet-field"}));stepDiv.append(select)}if(!values)return select;var meta=metadata[values.lang];var codes=meta&&meta.g||[];var prevVal=select.val();var choices=$.map(codes,function(code){return[{code:code,label:genderLabel(code)}]});populateMenus(select,choices,null,"Cinsiyetê hilbijêre");if($.grep(choices,function(c){return c.code===prevVal}).length){select.val(prevVal)}return select},output:function(){return""}},lektur:{id:"lektur",name:"Cureyê lêkerê (fiîlê)",question_fmt:"Cureyê lêkerê (fiîlê) çi ye?",question_br:true,visible:function(values){return values.lang&&values.pos&&/^lêker/.test(values.pos)},choices:[["gh","gerguhêz"],["x-gh","xwerû-gerguhêz"],["p-gh","pêkhatî-gerguhêz"],["h-gh","hevedudanî-gerguhêz"],["b-gh","biwêjî-gerguhêz"],["ngh","negerguhêz"],["x-ngh","xwerû-negerguhêz"],["p-ngh","pêkhatî-negerguhêz"],["h-ngh","hevedudanî-negerguhêz"],["b-ngh","biwêjî-negerguhêz"]],output:function(){return""}},tr:{id:"tr",name:"transkrîpsiyon",question_fmt:"Transkrîpsiyona kelîmeyê",multiple:false,narrow:true,visible:function(values){return values.lang&&values.pos&&needsTransliteration(values.lang)}},dfn:{id:"dfn",name:"Mane",question_fmt:"Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!",multiple:true,ordered:true,rows:3,visible:function(values){return values.lang&&values.pos},example:function(values){var examples=steps.dfn.posExamples||{};var full=values.pos||"";return examples[full]||""},buildSenseBlock:function(stepDiv){var block=$("<li class='wiktku-dfn-sense'></li>");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)});var tplDefs=steps.dfn.tplDefs||{};var _allTplOptions=[];var tplSelect=$(mw.html.element("select",{"class":"wiktku-dfn-tpl-select"}));tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));$.each(tplDefs,function(name,tpl){var opt=$("<option></option>").val(name).text(name).attr("data-langs",tpl.langs?tpl.langs.join(","):"").attr("data-nolang",tpl.noLang?"1":"");tplSelect.append(opt);_allTplOptions.push(opt[0])});function updateTplOptions(){var lang=getValues().lang||"";var currentVal=tplSelect.val();var currentStillValid=false;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}});if(!isMobile)tplSelect.trigger("chosen:updated");if(currentVal&&!currentStillValid){tplSelect.val("");dfnArea.show();tplTitleInput.hide().val("")}}tplSelect.data("updateOptions",updateTplOptions);var tplTitleInput=$("<input type='text'>").addClass("wiktku-dfn-tpl-title wiktku-start-narrow").attr("placeholder","Navê rûpelê…").hide();tplTitleInput.on("input",function(){steps.dfn.onDfnInput(stepDiv,block,this)});function updateFieldVisibility(){var isTpl=!!tplSelect.val();$.each(HIDDEN_ON_TPL_GROUPS,function(_,cls){block.find("."+cls+"-group").toggle(!isTpl)})}block.data("updateFieldVisibility",updateFieldVisibility);tplSelect.on("change",function(){var chosen=$(this).val();if(chosen){dfnArea.hide();tplTitleInput.show().focus()}else{dfnArea.show();tplTitleInput.hide().val("")}updateFieldVisibility();refresh()});var tplRow=$("<div></div>").addClass("wiktku-dfn-tpl-row").append(tplSelect).append(" ").append(tplTitleInput);block.append(tplRow);block.append(dfnArea);updateTplOptions();var allGroups=[{cls:"wiktku-dfn-etiket",label:"Etîket",rows:1,narrow:true,izah:"xisûsen, Mêrdîn, anatomî, Badînî"},{cls:"wiktku-dfn-kategori",label:"Kategorî",rows:1,narrow:true,izah:"wekî etîket lê xuya nabe tenê kategorîze dike"},{cls:"wiktku-dfn-alternativ",label:"Formên alternatîv",rows:1,narrow:true,izah:"Varyantên vê kelîmeyê"},{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-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"},{cls:"wiktku-dfn-jornav",label:"Jornav",rows:1,narrow:true,izah:"Heywan jornavê guhandar e"},{cls:"wiktku-dfn-jernav",label:"Jêrnav",rows:1,narrow:true,izah:"Guhandar jêrnavê heywan e"},{cls:"wiktku-dfn-nimune",label:"Nimûne",rows:2,narrow:false}];$.each(allGroups,function(_,g){var groupDiv=$("<div></div>").addClass("wiktku-dfn-subgroup "+g.cls+"-group");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(){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","Transkrîpsiyona 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 kurmancî"))}if(subWrap.children().length)li.append(subWrap);return li}if(g.cls==="wiktku-dfn-nimune"){var lbl=$("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label+": ");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{var lbl=$("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label+": "+g.izah);var list=$("<ol></ol>").addClass("wiktku-dfn-sublist "+g.cls+"-list");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",g.label+"…");firstInput.on("input",function(){steps.dfn.onSubInput(list,this,g.rows,g.narrow)});list.append($("<li></li>").append(firstInput));groupDiv.append(lbl).append(list);block.append(groupDiv)}});(function(){var jederGroupDiv=$("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");var jederLbl=$("<span></span>").addClass("wiktku-dfn-sublabel").text("Jêgirtin: ");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");var dragHandle=$("<span></span>").addClass("wiktku-jeder-drag-handle").attr("title","Bişkûne û bikişîne da ku rêzê biguhezîne");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(){var val=$.trim($(this).val());var isLast=li.is(jederList.children("li:last-child"));if(val&&isLast){jederList.append(buildJederLi())}});li.append(dragHandle).append(quoteTa);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","Transkrîpsiyona 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);var citoidWrap=$("<div></div>").addClass("wiktku-dfn-quote-citoid");var citoidInput=$("<input type='text'>").addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow").attr("placeholder","URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");var citoidBtn=new OO.ui.ButtonWidget({label:"Bi Citoid bîne",classes:["wiktku-dfn-quote-citoid-btn"]});var citoidStatus=$("<span></span>").addClass("wiktku-dfn-quote-citoid-status");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.citoidHelper.ensure().then(function(){return 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__";if(srcSel.val()!==targetVal){srcSel.val(targetVal).trigger("change")}if(hasTpl){$.each(result.fields,function(key,val){var input=srcFields.find("[data-field-key='"+key+"']");if(input.length&&!$.trim(input.val())){input.val(val)}})}else{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){e.preventDefault();runCitoidFetch()}});citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);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))});var srcFields=$("<div></div>").addClass("wiktku-dfn-quote-src-fields");srcSel.on("change",function(){srcFields.empty();var chosen=$(this).val();if(!chosen)return;if(chosen==="__manual__"){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__"){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","Navê şablonê lê 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…");function showCatResults(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()}function selectCatTpl(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+"…");row.append(fieldLbl).append(inp);srcFields.append(row)})});li.append(citoidWrap).append(srcSel).append(srcFields);return li}jederGroupDiv.data("buildJederLi",buildJederLi);jederList.append(buildJederLi());mw.loader.using("jquery.ui.sortable",function(){jederList.sortable({handle:".wiktku-jeder-drag-handle",axis:"y",items:"li:not(:last-child)",tolerance:"pointer"})});jederGroupDiv.append(jederLbl).append(jederList);block.append(jederGroupDiv)})();updateFieldVisibility();return block},onDfnInput:function(stepDiv,block,textarea){var val=$.trim($(textarea).val());var list=stepDiv.find("ol.wiktku-dfn-list");var isLast=block.is(list.children("li:last-child"));if(val&&isLast){var newBlock=steps.dfn.buildSenseBlock(stepDiv);list.append(newBlock);if(!isMobile)newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions)}refresh()},onSubInput:function(list,input,rows,narrow){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)});list.append($("<li></li>").append(next))}},tplDefs:{},posExamples:{},jederDefs:{},build:function(stepDiv){if(stepDiv.find("ol.wiktku-dfn-list").length)return null;var list=$("<ol class='wiktku-dfn-list'></ol>");list.append(steps.dfn.buildSenseBlock(stepDiv));stepDiv.append(list);return null},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()||"";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}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}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__"){}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;var hasMane=senses.some(function(s){return s.mane!==""||s.tpl&&s.tplTitle||senseHasExtraContent(s)});var hasRegularMane=senses.some(function(s){return!s.tpl&&s.mane!==""});var defLines=[];var citeLines=[];$.each(senses,function(_,sense){if(sense.tpl){if(!sense.tplTitle)return;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);return}var dfn=sense.mane;if(!dfn&&!senseHasExtraContent(sense))return;if(dfn){dfn=stripWikilinks(dfn);if(dfn[0]!==dfn[0].toUpperCase())dfn=capitalize(dfn);if(!dfn.match(/[.…?!)\]]$/))dfn+="."}else{dfn="{{mane?|"+lang+"}}"}var katTpl=buildSenseTpl(sense,"kategori",lang,"K");var labelTpl=buildSenseTpl(sense,"etiket",lang,"e");var out="# "+(katTpl?katTpl+" ":"")+(labelTpl?labelTpl+" ":"")+dfn;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);var idx=defLines.length;defLines.push(out+(cite?"{{wiktku-cite-"+idx+"}}":""));citeLines.push(cite)});function restoreCites(t){return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g,function(_,n){return citeLines[parseInt(n,10)]||""})}var text=defLines.join("\n")+"\n";if(!hasMane){text="# {{mane?|"+lang+"}}\n#* {{bênimûne|"+lang+"}}\n";callback(text);return}if(!hasRegularMane){callback(restoreCites(text));return}var didTryToLoad=function(){if(typeof $.wiktviLinkify==="function"){$.wiktviLinkify(text,function(linked){callback(restoreCites(linked))})}else{callback(restoreCites(text))}};mw.loader.using("ext.gadget.linkify",didTryToLoad,didTryToLoad)}},je:makeWordListStep("je","jê","Kelîmeyên jê çêbûyî binivîse","Jê","g"),tekildar:makeWordListStep("tekildar","têkildar","Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse","Têkildar","g"),binereherwiha:makeWordListStep("binereherwiha","binêre herwiha","Kelîmeyên din ên eleqedar binivîse","Binêre herwiha","g"),etym:{id:"etym",name:"etîmolojî",question_fmt:"Etîmolojiya kelîmeyê binivîse",etymTpls:{},visible:function(values){return values.lang&&values.pos},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))});sel.trigger("chosen:updated")},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","Nimûne: Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");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("chosen:updated");ta.focus()});if(isMobile){wrap.append(wrapNativeSelect(sel))}else{wrap.append(sel)}wrap.append($("<br>")).append(ta);stepDiv.append(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"}var dfnSenses=steps.dfn.getSenses($("#wiktku-start-dfn"));var hasRegularMane=dfnSenses.some(function(s){return!s.tpl&&(s.mane!==""||senseHasExtraContent(s))});if(!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:{navz:true},buildRow:function(stepDiv){var row=$("<li class='wiktku-dunde-row wiktku-lang-row'></li>");var langSel=$(mw.html.element("select",{"class":"wiktku-start-step-keyfield wiktku-start-narrow"}));langSel.attr("data-placeholder","Zimanekî lê bigere");populateMenus(langSel,steps.dunde.keys||[],steps.dunde.disabledKeys);langSel.on("change",function(){steps.dunde.onLangChange(stepDiv,row)});var wordList=$("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");var firstWord=steps.dunde.makeWordInput();wordList.append($("<li></li>").append(firstWord));if(isMobile){row.append(wrapNativeSelect(langSel)).append(wordList)}else{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);if(!isMobile)newRow.find("select").chosen(chosenOptions)}steps.dunde.syncDisabledKeys(stepDiv)},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("chosen:updated")})},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);if(!isMobile)list.find("select").chosen(chosenOptions);return null},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",visible:function(values){return steps.trans.visible(values)},disabledKeys:{navz:true},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 wiktku-plain-select"}));populateMenus(langSel,steps.zarava.keys||[],steps.zarava.disabledKeys,"Zarava hilbijêre");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)}steps.zarava.syncDisabledKeys(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)})})},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);return group},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},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},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"";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)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");if(!wordLines)return undefined;var block="{{werger-ser|"+(gloss.text||"?")+"|"+gloss.index+"}}";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",visible:function(values){if(!(values.lang==="ku"&&values.pos))return false;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},buildRow:function(group){var row=$("<li class='wiktku-trans-row wiktku-lang-row'></li>");var langSel=$(mw.html.element("select",{"class":"wiktku-start-step-keyfield wiktku-start-narrow"}));langSel.attr("data-placeholder","Zimanekî lê bigere");populateMenus(langSel,steps.trans.keys||[],steps.trans.disabledKeys);langSel.on("change",function(){steps.trans.onLangChange(group,row)});var wordList=$("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");var firstWord=steps.trans.makeWordInput();wordList.append($("<li></li>").append(firstWord));if(isMobile){row.append(wrapNativeSelect(langSel)).append(wordList)}else{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);if(!isMobile)newRow.find("select").chosen(chosenOptions)}steps.trans.syncDisabledKeys(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("chosen:updated")})},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);if(!isMobile)list.find("select").chosen(chosenOptions);return group},build:function(stepDiv){var glosses=steps.trans.glossList();if(!glosses.length){stepDiv.find(".wiktku-trans-group").remove();return null}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);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},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},getTranslations:function(stepDiv){var result=[];stepDiv.find(".wiktku-trans-group").each(function(){result.push(steps.trans.getGroupTranslations($(this)))});return result},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}var dfn=s.mane;if(!dfn){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();if(!glosses.length)return"";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):[];if(!gloss.text&&!rows.length)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==== Werger ====\n"+blocks.join("\n")+"\n"}},cavkani:{id:"cavkani",name:"çavkanî",question_fmt:"Çavkaniyên din ên (ne jêgirtinan) lê zêde bike",visible:function(values){return!!values.pos},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))});sel.trigger("chosen:updated")},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 jêrê hilbijêre an rasterast binivîse…");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>");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.")})}sel.on("change",function(){var name=$(this).val();if(!name)return;insertAtCursor(ta[0],"\n* {{"+name+"}}");$(this).val("");sel.trigger("chosen:updated");ta.focus()});if(isMobile){wrap.append(wrapNativeSelect(sel))}else{wrap.append(sel)}wrap.append(status).append($("<br>")).append(ta);stepDiv.append(wrap);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);var ordered_steps=[steps.lang,steps.pron,steps.pos,steps.cinsiyet,steps.lektur,steps.tr,steps.dfn,steps.je,steps.tekildar,steps.etym,steps.dunde,steps.binereherwiha,steps.zarava,steps.trans,steps.cavkani];var PER_SECTION_STEP_IDS=["pos","cinsiyet","lektur","tr","dfn","je","tekildar","etym","dunde","binereherwiha","zarava","trans","cavkani"];var sections=[{}];var activeSection=0;function snapshotDfn(){return steps.dfn.getSenses($("#wiktku-start-dfn"))}function restoreDfn(senses){var stepDiv=$("#wiktku-start-dfn");stepDiv.find("ol.wiktku-dfn-list").remove();steps.dfn.build(stepDiv);var list=stepDiv.find("ol.wiktku-dfn-list");$.each(senses,function(i,sense){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()}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()}else{block.find("textarea.wiktku-dfn-mane").val(sense.mane||"").show();block.find("input.wiktku-dfn-tpl-title").hide().val("")}var updateVis=block.data("updateFieldVisibility");if(updateVis)updateVis();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;$.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)});subList.append($("<li></li>").append(ta))}})}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)});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);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)});if(buildFn)nimuneList.append(buildFn())}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);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){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)});if(buildJederFn)jederList.append(buildJederFn())}})}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}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}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()}});sections[activeSection]=snap}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==="trans"){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;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{var extraInput=steps.trans.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.trans.makeWordInput()))}})});return}if(id==="zarava"){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;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"){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{var extraInput=steps.dunde.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.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)}if(!isMobile)fields.filter("select").trigger("chosen:updated")})}function getWikitextForCurrentSection(callback){var values=getValues();var entry=steps.pos.output(values);steps.dfn.getOutput(values,function(output){entry+=output;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);callback(entry,values)})}function getWikitextAllSections(callback){saveCurrentSection();var results=new Array(sections.length);var sharedValues=getValues();function processSection(i){if(i>=sections.length){restoreSection(sections[activeSection]);refresh();callback(results,sharedValues);return}restoreSection(sections[i]);refresh();getWikitextForCurrentSection(function(sectionText){results[i]=sectionText;processSection(i+1)})}processSection(0)}function getWikitext(callback){getWikitextAllSections(function(sectionTexts,sharedValues){var entry=steps.lang.output(sharedValues);entry+=steps.pron.output(sharedValues);entry+=sectionTexts.join("\n");var dfnSenses=sections[0]&&sections[0].dfn||[];var dfnSummary=dfnSenses.map(function(s){return s.mane}).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;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){var displayText=capitalize(choice.label);if(choice.sortKey!==undefined){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]})}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);if(isMobile){populateMenus(input,step.keys,getDisabledKeys(step));return wrapNativeSelect(input)}input.on("chosen:showing_dropdown",function(){$(this).data("wiktku-defaultValue",$(this).val())});input.change(function(_,context){var inputs=$("#wiktku-start-"+step.id+" .wiktku-start-step-keyfield").not(this);var oldValue=$(this).data("wiktku-defaultValue");var newValue=context.selected;inputs.find("option[value='"+oldValue+"']").prop("disabled",false);inputs.find("option[value='"+newValue+"']").prop("disabled",true);inputs.trigger("chosen:updated")});populateMenus(input,step.keys,getDisabledKeys(step));return input}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);nextInput.on("input",function(evt){refreshForTextField(evt.target,step)});var nextKeyInput=buildKeyInput(step);var nextItem=$("<li></li>").append(nextKeyInput).append(nextInput);item.parent().append(nextItem);if(nextKeyInput)nextKeyInput.chosen(chosenOptions)}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)}function refresh(evt){var values=getValues();var changedStep=evt&&evt.data;var didFocus=false;var canContinue=true;$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function(){var fn=$(this).data("updateOptions");if(typeof fn==="function")fn()});steps.pron.refreshTplSelect();steps.etym.refreshTplSelect();steps.cavkani.refreshTplSelect();(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);var subWrap=li.find(".wiktku-dfn-nimune-sub");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","Transkrîpsiyon…");subWrap.prepend(trTa)}else if(!showTr&&trTa.length){trTa.remove()}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ê kurmancî…");subWrap.append(tTa)}else if(!showT&&tTa.length){tTa.remove()}subWrap=li.find(".wiktku-dfn-nimune-sub");if(subWrap.length&&!subWrap.children().length)subWrap.remove()})})();(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);var subWrap=li.find(".wiktku-dfn-quote-sub");var trTa=li.find(".wiktku-dfn-nimune-quote-tr");if(showTr&&!trTa.length){if(!subWrap.length){subWrap=$("<div></div>").addClass("wiktku-dfn-quote-sub");li.find(".wiktku-dfn-nimune-quote").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","Transkrîpsiyona jêgirtinê…");subWrap.prepend(trTa)}else if(!showTr&&trTa.length){trTa.remove()}var tTa=li.find(".wiktku-dfn-nimune-quote-t");if(showT&&!tTa.length){if(!subWrap.length){subWrap=$("<div></div>").addClass("wiktku-dfn-quote-sub");li.find(".wiktku-dfn-nimune-quote").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ê ya kurmancî…");subWrap.append(tTa)}else if(!showT&&tTa.length){tTa.remove()}subWrap=li.find(".wiktku-dfn-quote-sub");if(subWrap.length&&!subWrap.children().length)subWrap.remove()})})();$.each(steps,function(stepId,step){if(typeof step.visible==="function"&&!step.visible(values)){$("#wiktku-start-"+stepId).slideUp()}else{var stepDiv=$("#wiktku-start-"+stepId);if(changedStep!==step&&typeof step.build==="function"){prepareInput(step.build(stepDiv,values),step)}var input=stepDiv.find(".wiktku-start-step-field");if(typeof step.example==="function"){var exampleText=step.example(values)||(step.id==="dfn"?"Mane…":"");input.attr("placeholder",exampleText)}var didSlideDown;if(!didFocus&&stepDiv.css("display")==="none"&&step.required){didSlideDown=function(evt){input.focus()};didFocus=true}stepDiv.slideDown(didSlideDown)}if(step.required&&(values[stepId]===""||step.multiple&&values[stepId].join("")==="")){canContinue=false}});continueBtn.setDisabled(!canContinue)}function destroy(){$("#wiktku-start-form, .noafterwizard").slideUp(function(){$(this).remove()});$(".noarticletext .nowizard, #editform").slideDown();$("#ca-edit").removeClass("selected");$("#ca-wiktku-nowizard").addClass("selected")}var LANG_PRIORITY=["navz","ku","hac","sdh","lki","ckb","zza"];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){var sep=existing&&!/\n\s*$/.test(existing)?"\n\n":"";return existing+sep+wikitext}var firstSectionStart=firstMatch.index;var lineStart=existing.lastIndexOf("\n",firstSectionStart-1);var preambleEnd=lineStart!==-1?lineStart+1:0;preamble=existing.slice(0,preambleEnd);var body=existing.slice(preambleEnd);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]})}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;return getLangName(a).localeCompare(getLangName(b),userLang)}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);var joined=sections.map(function(s){return s.content.replace(/\n+$/,"")}).join("\n");return preamble+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 di rûpelê de 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(" Naveroka rûpelê paqij bike (rûpelê ji nû ve binivîse)");startForm.append($("<p class='wiktku-addlang-notice'></p>").append(clearPageCheck).append(clearPageLabel));clearPageCheck.on("change",function(){refresh()})}var spinner;var sectionTabsDiv=$("<div id='wiktku-section-tabs'></div>");startForm.submit(function(evt){evt.preventDefault();continueBtn.setDisabled(true);spinner.show();getWikitext(function(wikitext){if(addLangMode&&clearPageCheck&&clearPageCheck.prop("checked")){$("#wpTextbox1").val(wikitext)}else if(addLangMode){var textbox=$("#wpTextbox1");var existing=textbox.val()||"";var langHeaderMatch=wikitext.match(new RegExp("^("+langHeaderSource("[^}]+")+"\\n)"));if(langHeaderMatch){var langHeader=langHeaderMatch[1];var newCode=langHeaderMatch[2];var existingHeader=findLangHeader(existing,newCode);if(existingHeader){var newContent=wikitext.slice(langHeader.length);var searchFrom=existingHeader.index+existingHeader.match.length;var nextL2=existing.indexOf("\n== ",searchFrom);var insertAt=nextL2!==-1?nextL2:existing.length;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{textbox.val(insertNewLangSection(existing,wikitext))}}else{textbox.val(insertNewLangSection(existing,wikitext))}}else{$("#wpTextbox1").val(wikitext)}$("html, body").animate({scrollTop:0});callToAction.text("Hindik ma!");$("#wiktku-preview-container").remove();destroy()})});$.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);stepDiv.append($(mw.html.element("h3",{"class":"wiktku-start-step-name"},new mw.html.Raw(mw.html.element("label",{"for":"wiktku-start-"+step.id+"-field"},name)))));if(step.question_fmt){stepDiv.append($(mw.html.element("label",{"class":"wiktku-start-step-question","for":"wiktku-start-"+step.id+"-field"},step.question_fmt)));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);if(isMobile){stepDiv.append(wrapNativeSelect(input))}else{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.multiple){input.on("input",function(evt){refreshForTextField(evt.target,step)});var keyInput=buildKeyInput(step);var list=$(step.ordered?"<ol></ol>":"<ul></ul>").append($("<li></li>").append(keyInput).append(input));stepDiv.append(list)}else stepDiv.append(input)}prepareInput(input,step);stepDiv.hide();if(step.id==="pos")startForm.append(sectionTabsDiv);startForm.append(stepDiv)});var addSectionBtn=new OO.ui.ButtonWidget({label:"+ Beş zêde bike",disabled:true});addSectionBtn.on("click",function(){saveCurrentSection();sections.push({});activeSection=sections.length-1;$.each(PER_SECTION_STEP_IDS,function(_,id){if(id==="dfn"){var stepDiv=$("#wiktku-start-dfn");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==="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("");if(!isMobile)stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated")});renderSectionTabs();refresh();$("html, body").animate({scrollTop:sectionTabsDiv.offset().top-20},300)});function renderSectionTabs(){addSectionBtn.$element.detach();sectionTabsDiv.empty();var lang=getValues().lang||"";if(!lang){sectionTabsDiv.hide();return}sectionTabsDiv.show();$.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);(function(idx){tab.on("click",function(){if(idx===activeSection)return;saveCurrentSection();activeSection=idx;restoreSection(sections[activeSection]);refresh();renderSectionTabs()})})(i);sectionTabsDiv.append(tab)});var activePos=sections[activeSection]&&sections[activeSection].pos?sections[activeSection].pos:getValues().pos;addSectionBtn.setDisabled(!activePos);sectionTabsDiv.append(addSectionBtn.$element)}renderSectionTabs();function restoreFromCache(cached){if(cached.lang){$("#wiktku-start-lang-field").val(cached.lang);if(!isMobile)$("#wiktku-start-lang-field").trigger("chosen:updated")}refresh();if(cached.pron!==undefined){$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron)}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();mw.notify("Danûstandina berê ya tomarkirî ji cache hate barkirin.",{type:"info",tag:"wiktku-cache-restore"})}startForm.on("change","#wiktku-start-pos-field",function(){renderSectionTabs()});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(){mw.notify("Tê şandin…",{type:"info",tag:"wiktku-submit"});startForm.trigger("submit")});footer.append(continueBtn.$element);var sourceEditorBtn=new OO.ui.ButtonWidget({label:"Edîtora çavkaniyê veke"});sourceEditorBtn.on("click",function(){destroy()});footer.append(sourceEditorBtn.$element);var saveCacheBtn=new OO.ui.ButtonWidget({label:"Li cache tomar bike",title:"Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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();var snapshot={lang:getValues().lang||"",pron:$("#wiktku-start-pron .wiktku-pron-text").val()||"",sections:sections,activeSection:activeSection};try{formCache.Set(snapshot);mw.notify("Rewşa formê li cache hate tomarkirin.",{type:"success",tag:"wiktku-cache-save"})}catch(e){mw.notify("Tomarkirina li cache serneket.",{type:"error",tag:"wiktku-cache-save"})}});footer.append(saveCacheBtn.$element);var clearCacheBtn=new OO.ui.ButtonWidget({label:"Cache paqij bike",flags:["destructive"],title:"Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)."});clearCacheBtn.on("click",function(){formCache.Set({});mw.notify("Cache hate paqijkirin.",{type:"success",tag:"wiktku-cache-clear"})});footer.append(clearCacheBtn.$element);var previewCheck=$("<input type='checkbox' id='wiktku-preview-toggle'>");var previewLabel=$("<label for='wiktku-preview-toggle'></label>").text(" Pêşdîtina wîkîtekstê");var previewPanel=$("<pre id='wiktku-preview-panel'></pre>").hide();var livePreviewCheck=null;var livePreviewPanel=null;if(!isMobile){livePreviewCheck=$("<input type='checkbox' id='wiktku-live-preview-toggle'>");livePreviewPanel=$("<div id='wiktku-live-preview-panel'></div>").hide()}var previewContainer=$("<div id='wiktku-preview-container'></div>");previewContainer.append(previewPanel);if(!isMobile)previewContainer.append(livePreviewPanel);var previewTimer=null;var livePreviewIntervalId=-1;var livePreviewActive=false;function schedulePreview(){if(!previewCheck.prop("checked"))return;clearTimeout(previewTimer);previewTimer=setTimeout(function(){getWikitext(function(wikitext){previewPanel.text(wikitext).show();updatePanelLayout()})},400)}function loadLivePreview(){getWikitext(function(wikitext){(new mw.Api).parse(wikitext,{title:mw.config.get("wgPageName")}).done(function(html){if(!livePreviewActive)return;livePreviewPanel.html(html).show();updatePanelLayout()})})}function updatePanelLayout(){var wikitextVisible=previewCheck.prop("checked");var liveVisible=livePreviewCheck&&livePreviewCheck.prop("checked");if(wikitextVisible&&liveVisible){previewPanel.css({flex:"1 1 45%","min-width":"280px"});livePreviewPanel.css({flex:"1 1 45%","min-width":"280px"})}else{if(wikitextVisible)previewPanel.css({flex:"1 1 100%","min-width":""});if(liveVisible&&livePreviewPanel)livePreviewPanel.css({flex:"1 1 100%","min-width":""})}}previewCheck.on("change",function(){if($(this).prop("checked")){schedulePreview()}else{clearTimeout(previewTimer);previewPanel.hide();updatePanelLayout()}});if(!isMobile){livePreviewCheck.on("change",function(){if($(this).prop("checked")){livePreviewActive=true;livePreviewPanel.show();loadLivePreview();livePreviewIntervalId=setInterval(loadLivePreview,500)}else{livePreviewActive=false;clearInterval(livePreviewIntervalId);livePreviewIntervalId=-1;livePreviewPanel.hide().empty();updatePanelLayout()}})}startForm.on("input change",function(){schedulePreview()});footer.append($("<span style='margin-left:1em'></span>").append(previewCheck).append(previewLabel));if(!isMobile){var livePreviewLabel=$("<label for='wiktku-live-preview-toggle'></label>").text(" Pêşdîtina zindî");footer.append($("<span style='margin-left:1em'></span>").append(livePreviewCheck).append(livePreviewLabel))}startForm.append(footer);$("#editform").hide().before(startForm);startForm.after(previewContainer);if(!isMobile)startForm.find("select").not(".wiktku-plain-select").chosen(chosenOptions);refresh();var pinnedChoices=steps.lang.choices.slice();var pinnedCodes=$.map(pinnedChoices,function(choice){return choice.code});zimanJsonPromise.then(function(data){var page=data.query.pages[0];var content=page&&page.revisions&&page.revisions[0].slots.main.content;var zimanData;try{zimanData=JSON.parse(content)}catch(e){zimanData={}}var extraChoices=[];$.each(zimanData,function(code,name){if(/-pro$/i.test(code))return;if(/ziman[eê]\s+destan/i.test(name))return;if(pinnedCodes.indexOf(code)!==-1)return;var sortKey=name.replace(/^ziman[eê]\s+/i,"");var choice={code:code,label:name,sortKey:sortKey};extraChoices.push(choice);langsByCode[code]=choice});$.each(pinnedChoices,function(i,choice){if(choice.sortKey===undefined)choice.sortKey=choice.label;langsByCode[choice.code]=choice});extraChoices.sort(function(a,b){return a.sortKey.localeCompare(b.sortKey,userLang)});var allChoices=pinnedChoices.concat(extraChoices);steps.lang.choices=allChoices;steps.trans.keys=allChoices;steps.dunde.keys=allChoices;steps.zarava.keys=getZaravaKeys(allChoices);var langMenu=$("#wiktku-start-lang-field");populateMenus(langMenu,steps.lang.choices);if(!isMobile)langMenu.chosen("destroy").chosen(chosenOptions);langMenu.trigger("chosen:updated");var transMenus=$("#wiktku-start-trans .wiktku-start-step-keyfield");populateMenus(transMenus,steps.trans.keys,getDisabledKeys(steps.trans));if(!isMobile)transMenus.chosen("destroy").chosen(chosenOptions);transMenus.trigger("chosen:updated");var dundeMenus=$("#wiktku-start-dunde .wiktku-start-step-keyfield");populateMenus(dundeMenus,steps.dunde.keys,getDisabledKeys(steps.dunde));if(!isMobile)dundeMenus.chosen("destroy").chosen(chosenOptions);dundeMenus.trigger("chosen:updated");var zaravaMenus=$("#wiktku-start-zarava .wiktku-start-step-keyfield");populateMenus(zaravaMenus,steps.zarava.keys,getDisabledKeys(steps.zarava),"Zarava hilbijêre");var cachedDraft=formCache.Get();if(cachedDraft&&cachedDraft.sections&&cachedDraft.sections.length){restoreFromCache(cachedDraft)}})}mw.loader.using(["jquery.chosen","oojs-ui-core"],function(){metaApiPromise.then(function(data){var page=data.query.pages[0];var content=page&&page.revisions&&page.revisions[0].slots.main.content;var d;try{d=JSON.parse(content)}catch(e){d={}}if(d.metadata)$.extend(metadata,d.metadata);if(d.genderLabels)$.extend(GENDER_BASE_LABELS,d.genderLabels);if(d.langChoices)steps.lang.choices=choicesFromObject(d.langChoices,true);if(d.posChoices)steps.pos.choices=choicesFromObject(d.posChoices,false);if(d.tplDefs)steps.dfn.tplDefs=d.tplDefs;if(d.posExamples)steps.dfn.posExamples=d.posExamples;if(d.jederDefs)steps.dfn.jederDefs=d.jederDefs;if(d.pronTpls){steps.pron.pronTpls=d.pronTpls;steps.pron.refreshTplSelect()}if(d.etymTpls){steps.etym.etymTpls=d.etymTpls;steps.etym.refreshTplSelect()}steps.trans.keys=steps.lang.choices;steps.dunde.keys=steps.lang.choices;steps.zarava.keys=getZaravaKeys(steps.lang.choices)}).always(build)})}); ea3wt1rauq9kiw5zty7tcovdmyk5g44 6291594 6291593 2026-07-26T19:13:49Z Wikihez 7344 Restored revision 6291592 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]]) 6291594 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync; // otherwise (e.g. a "pick to insert" dropdown resetting itself // after use) clear the search box back to blank/placeholder. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } else { textInput.val(""); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; // Converts a plain {code: label} object (the format used for langChoices // and posChoices in data.json) into the ordered array of choice objects // ({code, label[, sortKey]}) that the rest of this script works with. // isLang: true adds a sortKey (defaults to label) and tells populateMenus // to show "(code)" after the label — used for 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field wiktku-plain-select", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [{ code: code, label: genderLabel(code) }]; }); populateMenus(select, choices, null, "Cinsiyetê hilbijêre"); if ($.grep(choices, function (c) { return c.code === prevVal; }).length) { select.val(prevVal); } return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. choices: [ ["gh", "gerguhêz"], ['x-gh', 'xwerû-gerguhêz'], ['p-gh', 'pêkhatî-gerguhêz'], ['h-gh', 'hevedudanî-gerguhêz'], ['b-gh', 'biwêjî-gerguhêz'], ["ngh", "negerguhêz"], ['x-ngh', 'xwerû-negerguhêz'], ['p-ngh', 'pêkhatî-negerguhêz'], ['h-ngh', 'hevedudanî-negerguhêz'], ['b-ngh', 'biwêjî-negerguhêz'] ], output: function () { return ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (name, tpl) { var opt = $("<option></option>") .val(name) .text(name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has changed. sel.trigger("chosen:updated"); }, 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", "Nimûne: 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("chosen:updated"); ta.focus(); }); if (isMobile) { wrap.append(wrapNativeSelect(sel)); } else { wrap.append(sel); } wrap.append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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 wiktku-plain-select" })); populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys, "Zarava hilbijêre"); 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); } 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); }); }); }, /** 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); 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 ""; 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) 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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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", 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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 ên (ne jêgirtinan) 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has changed. sel.trigger("chosen:updated"); }, 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 jêrê 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>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); sel.trigger("chosen:updated"); ta.focus(); }); if (isMobile) { wrap.append(wrapNativeSelect(sel)); } else { wrap.append(sel); } wrap.append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke" }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").not(".wiktku-plain-select").chosen(chosenOptions); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice.code; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = { code: code, label: name, sortKey: sortKey }; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey. if (choice.sortKey === undefined) choice.sortKey = choice.label; langsByCode[choice.code] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a.sortKey.localeCompare(b.sortKey, userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); if (!isMobile) langMenu.chosen("destroy").chosen(chosenOptions); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); if (!isMobile) transMenus.chosen("destroy").chosen(chosenOptions); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); if (!isMobile) dundeMenus.chosen("destroy").chosen(chosenOptions); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava), "Zarava hilbijêre"); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — this block only needs to wait // on the UI modules itself. mw.loader.using(["jquery.chosen", "oojs-ui-core"], function () { // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). // data.json stores these as a plain {code: label} object. if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true); // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> 50o6vcgdtskh6qgub3b0cintshz549a 6291596 6291594 2026-07-26T19:32:15Z Wikihez 7344 6291596 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync; // otherwise (e.g. a "pick to insert" dropdown resetting itself // after use) clear the search box back to blank/placeholder. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } else { textInput.val(""); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; // Converts a plain {code: label} object (the format used for langChoices // and posChoices in data.json) into the ordered array of choice objects // ({code, label[, sortKey]}) that the rest of this script works with. // isLang: true adds a sortKey (defaults to label) and tells populateMenus // to show "(code)" after the label — used for 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field wiktku-plain-select", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [{ code: code, label: genderLabel(code) }]; }); populateMenus(select, choices, null, "Cinsiyetê hilbijêre"); if ($.grep(choices, function (c) { return c.code === prevVal; }).length) { select.val(prevVal); } return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. // NOTE: populateMenus() expects {code, label} objects (like every // other step's choices) — plain [code, label] pairs leave // choice.code/choice.label undefined, so Chosen (desktop) ends up // with unusable options. 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 ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (name, tpl) { var opt = $("<option></option>") .val(name) .text(name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has changed. sel.trigger("chosen:updated"); }, 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", "Nimûne: 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("chosen:updated"); ta.focus(); }); if (isMobile) { wrap.append(wrapNativeSelect(sel)); } else { wrap.append(sel); } wrap.append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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: "Eger ev kelîme di zaravayên din de bi awayekî cuda tê gotin an nivîsandin, wan lê zêde bike", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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 wiktku-plain-select" })); populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys, "Zarava hilbijêre"); 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); } 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); }); }); }, /** 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); 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 ""; 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) 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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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: "Wergera 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); if (isMobile) { row.append(wrapNativeSelect(langSel)).append(wordList); } else { 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); // Init chosen after the new row is in the DOM (desktop only). if (!isMobile) newRow.find("select").chosen(chosenOptions); } // 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("chosen:updated"); }); }, // 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); // Init chosen after the select is in the DOM (desktop only). if (!isMobile) list.find("select").chosen(chosenOptions); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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 ên (ne jêgirtinan) 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has changed. sel.trigger("chosen:updated"); }, 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 jêrê 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>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); sel.trigger("chosen:updated"); ta.focus(); }); if (isMobile) { wrap.append(wrapNativeSelect(sel)); } else { wrap.append(sel); // build() is called again for every new beş (see the // "+ Beş zêde bike" handler), after the initial page-wide // .chosen() pass has already run — so this select needs // its own chosen() call or it stays a plain, unsearchable // native <select> on desktop. sel.chosen(chosenOptions); } wrap.append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile) { stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke" }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").not(".wiktku-plain-select").chosen(chosenOptions); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice.code; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = { code: code, label: name, sortKey: sortKey }; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey. if (choice.sortKey === undefined) choice.sortKey = choice.label; langsByCode[choice.code] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a.sortKey.localeCompare(b.sortKey, userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); if (!isMobile) langMenu.chosen("destroy").chosen(chosenOptions); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); if (!isMobile) transMenus.chosen("destroy").chosen(chosenOptions); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); if (!isMobile) dundeMenus.chosen("destroy").chosen(chosenOptions); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava), "Zarava hilbijêre"); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — this block only needs to wait // on the UI modules itself. mw.loader.using(["jquery.chosen", "oojs-ui-core"], function () { // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). // data.json stores these as a plain {code: label} object. if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true); // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> 7o511tlhrt252arz5vlnd2lttyonbh0 6291611 6291596 2026-07-26T21:27:35Z Wikihez 7344 6291611 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; function wrapNativeSelect(select) { var placeholder = select.attr("data-placeholder") || "Lê bigere…"; // Hide the native select — we drive it programmatically. select.hide(); var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); var textInput = $("<input type='search' autocomplete='off'>") .attr("placeholder", placeholder) .addClass("wiktku-start-narrow wiktku-ac-input"); var resultsList = $("<ul class='wiktku-ac-results'></ul>").hide(); // Full option cache — rebuilt when chosen:updated fires. var allOptions = []; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option allOptions.push({ value: v, text: $(this).text(), disabled: $(this).prop("disabled") }); }); } cacheOptions(); function showResults(query) { var q = query.trim().toLowerCase(); resultsList.empty(); var matched; if (q) { matched = []; $.each(allOptions, function (_, o) { if (o.disabled) return; var codeMatch = o.value.toLowerCase().indexOf(q) !== -1; var nameMatch = o.text.toLowerCase().indexOf(q) !== -1; if (codeMatch || nameMatch) { matched.push({ opt: o, codeMatch: codeMatch }); } }); // Code matches (especially prefix matches) come first. matched.sort(function (a, b) { var aCode = a.opt.value.toLowerCase(); var bCode = b.opt.value.toLowerCase(); var aPrefix = aCode.indexOf(q) === 0; var bPrefix = bCode.indexOf(q) === 0; if (aPrefix !== bPrefix) return aPrefix ? -1 : 1; if (a.codeMatch !== b.codeMatch) return a.codeMatch ? -1 : 1; return 0; }); matched = matched.map(function (item) { return item.opt; }); } else { matched = allOptions.filter(function (o) { return !o.disabled; }); } if (!matched.length) { resultsList.hide(); return; } $.each(matched.slice(0, 50), function (_, opt) { var li = $("<li></li>").text(opt.text).attr("data-value", opt.value); // 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); }); // Keep mousedown for desktop fallback. li.on("mousedown", function (e) { e.preventDefault(); selectOption(opt.value, opt.text); }); resultsList.append(li); }); // Position the list above or below depending on available 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%" }); resultsList.show(); } function selectOption(value, text) { textInput.val(text); select.val(value); resultsList.hide(); select.trigger("change"); } textInput.on("input focus", function () { showResults($(this).val()); }); textInput.on("blur", function () { // Short delay so a touchstart on a result item fires first. setTimeout(function () { resultsList.hide(); }, 200); }); // chosen:updated → re-cache (called after ziman.json loads). select.on("chosen:updated", function () { cacheOptions(); // If a value is already selected, keep the display text in sync; // otherwise (e.g. a "pick to insert" dropdown resetting itself // after use) clear the search box back to blank/placeholder. var cur = select.val(); if (cur) { var match = $.grep(allOptions, function (o) { return o.value === cur; })[0]; if (match) textInput.val(match.text); } else { textInput.val(""); } }); // No-op .chosen() so desktop call-sites don't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; // Converts a plain {code: label} object (the format used for langChoices // and posChoices in data.json) into the ordered array of choice objects // ({code, label[, sortKey]}) that the rest of this script works with. // isLang: true adds a sortKey (defaults to label) and tells populateMenus // to show "(code)" after the label — used for 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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, // Use the same code/name-priority autocomplete widget as mobile // on desktop too — jquery.chosen has no way to rank an exact/ // prefix code match (e.g. "ang" → Îngilîziya kevn) above // unrelated label substring matches, so plain Chosen search // buried the right result. See wrapNativeSelect's showResults(). forceCustomSearch: 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" : "")); } }); // Notify Chosen that the option list has changed (no-op on mobile). if (!isMobile) sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.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(""); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field wiktku-plain-select", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [{ code: code, label: genderLabel(code) }]; }); populateMenus(select, choices, null, "Cinsiyetê hilbijêre"); if ($.grep(choices, function (c) { return c.code === prevVal; }).length) { select.val(prevVal); } return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. // NOTE: populateMenus() expects {code, label} objects (like every // other step's choices) — plain [code, label] pairs leave // choice.code/choice.label undefined, so Chosen (desktop) ends up // with unusable options. 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 ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (name, tpl) { var opt = $("<option></option>") .val(name) .text(name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); if (!isMobile) tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Apply Chosen to the new block's template selector on desktop. if (!isMobile) newBlock.find("select.wiktku-dfn-tpl-select").chosen(chosenOptions); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has changed. sel.trigger("chosen:updated"); }, 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", "Nimûne: 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("chosen:updated"); ta.focus(); }); if (isMobile) { wrap.append(wrapNativeSelect(sel)); } else { wrap.append(sel); } wrap.append($("<br>")).append(ta); stepDiv.append(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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — uses the same code/name-priority // autocomplete widget as mobile on both platforms (jquery.chosen // can't rank code matches like "ang" above unrelated label // substring matches — see wrapNativeSelect's showResults()). var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow wiktku-plain-select" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); row.append(wrapNativeSelect(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); } // 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("chosen:updated"); }); }, 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); 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: "Eger ev kelîme di zaravayên din de bi awayekî cuda tê gotin an nivîsandin, wan lê zêde bike", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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 wiktku-plain-select" })); populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys, "Zarava hilbijêre"); 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); } 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); }); }); }, /** 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); 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 ""; 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) 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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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: "Wergera 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 — uses the same code/name-priority // autocomplete widget as mobile on both platforms (jquery.chosen // can't rank code matches like "ang" above unrelated label // substring matches — see wrapNativeSelect's showResults()). var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow wiktku-plain-select" })); langSel.attr("data-placeholder", "Zimanekî lê 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)); row.append(wrapNativeSelect(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); } // 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("chosen:updated"); }); }, // 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); 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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 ên (ne jêgirtinan) 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has changed. sel.trigger("chosen:updated"); }, 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 jêrê 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>"); 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."); }); } sel.on("change", function () { var name = $(this).val(); if (!name) return; insertAtCursor(ta[0], "\n* {{" + name + "}}"); $(this).val(""); sel.trigger("chosen:updated"); ta.focus(); }); if (isMobile) { wrap.append(wrapNativeSelect(sel)); } else { wrap.append(sel); // build() is called again for every new beş (see the // "+ Beş zêde bike" handler), after the initial page-wide // .chosen() pass has already run — so this select needs // its own chosen() call or it stays a plain, unsearchable // native <select> on desktop. sel.chosen(chosenOptions); } wrap.append(status).append($("<br>")).append(ta); stepDiv.append(wrap); 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify chosen widgets of the programmatic value change. if (!isMobile) fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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); if (isMobile) { populateMenus(input, step.keys, getDisabledKeys(step)); // On mobile return the wrapper; the select is inside it. // Change events still fire on the inner select. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput); item.parent().append(nextItem); if (nextKeyInput) nextKeyInput.chosen(chosenOptions); } 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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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); if (isMobile || step.forceCustomSearch) { // forceCustomSearch (desktop only): use the same code/name // autocomplete widget as mobile instead of jquery.chosen, // which has no way to rank code matches (e.g. "ang") above // unrelated label substring matches. Mark it plain-select // so the bulk .chosen() pass below skips the now-hidden // underlying <select>. if (step.forceCustomSearch && !isMobile) input.addClass("wiktku-plain-select"); stepDiv.append(wrapNativeSelect(input)); } else { 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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(""); if (!isMobile) stepDiv.find("select.wiktku-start-step-field").trigger("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); if (!isMobile) $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke" }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); if (!isMobile) startForm.find("select").not(".wiktku-plain-select").chosen(chosenOptions); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice.code; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = { code: code, label: name, sortKey: sortKey }; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey. if (choice.sortKey === undefined) choice.sortKey = choice.label; langsByCode[choice.code] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a.sortKey.localeCompare(b.sortKey, userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava), "Zarava hilbijêre"); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // jquery.chosen/oojs-ui-core load in parallel with metaApiPromise above // (mediawiki.api + the two API requests) — this block only needs to wait // on the UI modules itself. mw.loader.using(["jquery.chosen", "oojs-ui-core"], function () { // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). // data.json stores these as a plain {code: label} object. if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true); // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> 0pvsjsionmk5eyredbbcgvu44wkigal 6291627 6291611 2026-07-27T07:26:00Z Wikihez 7344 6291627 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; var chosenOptions = { width: "20em", disable_search_threshold: 0, placeholder_text_single: "Yekî hilbijêre", placeholder_text_multiple: "Hilbijêre", no_results_text: "Nehat dîtin", enable_split_word_search: true, search_contains: true }; // ---- Searchable-picker configuration ----------------------------------- // jquery.chosen renders its search results in plain option order and gives // no hook for re-ranking them, so it can never put an exact code match // ("ang" -> "Îngilîziya kevn") above the substring matches. Rather than // keep two pickers with two different behaviours, every platform now uses // the widget below (wrapNativeSelect), which does its own ranking. // // Flip useNativePicker to false to hand desktop back to Chosen. var useNativePicker = true; var useChosen = !useNativePicker && !isMobile; // ---- Diacritic-insensitive search -------------------------------------- // Lets "ingilizi" find "Îngilîzî", "kurdi" find "Kurdî", "sopani" // find "Şopanî". NFD decomposition handles every letter written as // base + combining accent (î ê û ç ş á é ö ...); // FOLD_EXTRA covers the handful Unicode cannot decompose that way. var FOLD_EXTRA = { "ı": "i", // dotless i "ø": "o", // o with stroke "đ": "d", // d with stroke "ð": "d", // eth "ə": "e", // schwa "ħ": "h", // h with stroke "ł": "l", // l with stroke "ŋ": "n", // eng "ʉ": "u", // u bar "ɨ": "i", // i bar "ʒ": "z", // ezh "ɛ": "e", // open e "ɔ": "o", // open 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; }); } // Baseline picker styling. Only the highlight rule is injected on mobile -- // the rest is desktop-only so it cannot override whatever // [[MediaWiki:Gadget-start.css]] already sets for the mobile layout. // Once that CSS page covers desktop too, this block can be deleted. (function injectPickerCSS() { if (!useNativePicker) return; if (document.getElementById("wiktku-ac-style")) return; var css = ".wiktku-ac-results li.wiktku-ac-active { background: #eaf3ff; }\n"; if (!isMobile) { css += ".wiktku-native-select-wrap { position: relative; display: inline-block; vertical-align: top; }\n" + ".wiktku-ac-input { box-sizing: border-box; width: 20em; max-width: 100%; padding: 4px 6px;" + " border: 1px solid #a2a9b1; border-radius: 2px; }\n" + ".wiktku-ac-input:focus { border-color: #36c; box-shadow: inset 0 0 0 1px #36c; outline: 0; }\n" + ".wiktku-ac-results { position: absolute; left: 0; z-index: 100; margin: 0; padding: 0;" + " list-style: none; width: 20em; max-width: 100%; overflow-y: auto; background: #fff;" + " border: 1px solid #a2a9b1; border-radius: 2px; box-shadow: 0 2px 4px rgba(0,0,0,.2); }\n" + ".wiktku-ac-results li { margin: 0; padding: 5px 8px; cursor: pointer; white-space: nowrap;" + " overflow: hidden; text-overflow: ellipsis; }\n" + ".wiktku-ac-results li:hover { background: #eaf3ff; }\n"; } var style = document.createElement("style"); style.id = "wiktku-ac-style"; style.appendChild(document.createTextNode(css)); document.head.appendChild(style); }()); /** * Replaces a native <select> with a searchable text input + ranked result * list. Returns a wrapper element containing the original (hidden) select, * so every call-site that reads .val() or listens for "change" on the * select keeps working unchanged. * * After changing the option list, trigger "chosen:updated" on the select to * re-cache -- the same event Chosen used, so existing call-sites still work. */ function wrapNativeSelect(select) { var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); // Multi-selects aren't supported by this widget, and a native multiple // <select> is perfectly usable as-is -- leave it alone. if (select.prop("multiple")) { return wrapper.append(select); } var placeholder = select.attr("data-placeholder") || "Le bigere..."; // Hide the native select -- we drive it programmatically. 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(); // Full option cache -- rebuilt when chosen:updated fires. var allOptions = []; // Options currently rendered, in display order, plus the keyboard cursor. var shown = []; var activeIndex = -1; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option var text = $(this).text(); // Language options read "Name (code)" -- score the name on its own // 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"), 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. This is the whole point of the widget: a // code that *is* the query, or begins with it, always outranks a name // that merely contains it -- so "ang" lists "Îngilîziya kevn (ang)" // first instead of burying it among every name containing "ang". 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 }); }); // Multi-word fallback (Chosen's enable_split_word_search): keep // anything whose label contains every token, e.g. "kevn ing". 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>") .text(opt.text) .attr("data-value", opt.value) .attr("aria-selected", "false"); // 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 () { setActive(idx); }); resultsList.append(li); }); positionList(); resultsList.show(); textInput.attr("aria-expanded", "true"); } function setActive(idx) { 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"); // Keep the cursor visible inside the scrollable list. 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 -- the behaviour Chosen had on desktop. 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); }); // chosen:updated -> re-cache (fired after ziman.json loads, after options // are enabled/disabled, and after programmatic .val() changes). select.on("chosen:updated", function () { cacheOptions(); syncInputToSelect(); if (resultsList.is(":visible")) showResults(textInput.val()); }); // No-op .chosen() so any stray desktop call-site doesn't need guarding. select.chosen = function () { return select; }; wrapper.append(textInput).append(resultsList).append(select); return wrapper; } /** * Turns every plain <select> under `root` into a searchable picker -- Chosen * when useChosen, otherwise the unified widget above. Selects marked * .wiktku-plain-select opt out, and the call is idempotent, so it is safe to * re-run after new rows or sense blocks are inserted. */ function initSelects(root) { var targets = $(root).find("select").addBack("select").not(".wiktku-plain-select"); if (useChosen) { targets.chosen(chosenOptions); return; } targets.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)); }); } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; // Converts a plain {code: label} object (the format used for langChoices // and posChoices in data.json) into the ordered array of choice objects // ({code, label[, sortKey]}) that the rest of this script works with. // isLang: true adds a sortKey (defaults to label) and tells populateMenus // to show "(code)" after the label — used for 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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" : "")); } }); // Notify the picker (or Chosen) that the option list changed. sel.trigger("chosen:updated"); }, build: function (stepDiv) { if (stepDiv.find(".wiktku-pron-wrap").length) return null; var wrap = $("<div class='wiktku-pron-wrap'></div>"); 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.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 too, so it goes back to its // placeholder instead of keeping the inserted template name. sel.trigger("chosen:updated"); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); initSelects(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field wiktku-plain-select", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [{ code: code, label: genderLabel(code) }]; }); populateMenus(select, choices, null, "Cinsiyetê hilbijêre"); if ($.grep(choices, function (c) { return c.code === prevVal; }).length) { select.val(prevVal); } return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. // NOTE: populateMenus() expects {code, label} objects (like every // other step's choices) — plain [code, label] pairs leave // choice.code/choice.label undefined, so Chosen (desktop) ends up // with unusable options. 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 ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (name, tpl) { var opt = $("<option></option>") .val(name) .text(name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); tplSelect.trigger("chosen:updated"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Give the new block's template selector the searchable picker. initSelects(newBlock); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has changed. sel.trigger("chosen:updated"); }, 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", "Nimûne: 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("chosen:updated"); ta.focus(); }); wrap.append(sel); wrap.append($("<br>")).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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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("chosen:updated"); }); }, 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: "Eger ev kelîme di zaravayên din de bi awayekî cuda tê gotin an nivîsandin, wan lê zêde bike", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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", "Zaravayekî lê bigere"); populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys, "Zarava hilbijêre"); 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("chosen:updated"); }); }, /** 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 ""; 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) 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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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: "Wergera 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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("chosen:updated"); }); }, // 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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 ên (ne jêgirtinan) 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has changed. sel.trigger("chosen:updated"); }, 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 jêrê 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("chosen:updated"); ta.focus(); }); wrap.append(sel); wrap.append(status).append($("<br>")).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); // Populate only now: refreshTplSelect() looks the select up by id // under #wiktku-start-cavkani, so running it before the wrap was // appended found nothing and bailed out — which left the dropdown // empty on every beş after the first, once the category members // were already cached. 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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); } // Notify the picker widgets of the programmatic value change. fields.filter("select").trigger("chosen:updated"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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); if (!useChosen) { populateMenus(input, step.keys, getDisabledKeys(step)); // Chosen got its disabled-key sync from the handler below, which // relies on Chosen's own change event payload. Plain change events // carry no payload, so recompute the taken set from the DOM instead. 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("chosen:updated"); }); }); // Return the wrapper; the select lives inside it and still fires change. return wrapNativeSelect(input); } input.on("chosen:showing_dropdown", function () { $(this).data("wiktku-defaultValue", $(this).val()); }); input.change(function (_, context) { var inputs = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").not(this); var oldValue = $(this).data("wiktku-defaultValue"); var newValue = context.selected; inputs.find("option[value='" + oldValue + "']").prop('disabled', false); inputs.find("option[value='" + newValue + "']").prop('disabled', true); inputs.trigger("chosen:updated"); }); populateMenus(input, step.keys, getDisabledKeys(step)); return 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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("chosen:updated"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, sections: Array, activeSection: number}} */ function restoreFromCache(cached) { if (cached.lang) { $("#wiktku-start-lang-field").val(cached.lang); $("#wiktku-start-lang-field").trigger("chosen:updated"); } // 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke" }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); // 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 (or Chosen, if useNativePicker is turned off). initSelects(startForm); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice.code; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = { code: code, label: name, sortKey: sortKey }; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey. if (choice.sortKey === undefined) choice.sortKey = choice.label; langsByCode[choice.code] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a.sortKey.localeCompare(b.sortKey, userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); // Chosen has to be torn down and rebuilt when the option list is // replaced; the native picker just needs the chosen:updated event. var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); if (useChosen) langMenu.chosen("destroy").chosen(chosenOptions); langMenu.trigger("chosen:updated"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); if (useChosen) transMenus.chosen("destroy").chosen(chosenOptions); transMenus.trigger("chosen:updated"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); if (useChosen) dundeMenus.chosen("destroy").chosen(chosenOptions); dundeMenus.trigger("chosen:updated"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava), "Zarava hilbijêre"); if (useChosen) zaravaMenus.chosen("destroy").chosen(chosenOptions); // Was missing entirely: without this the Zarava picker kept serving the // option list it cached before ziman.json replaced it. zaravaMenus.trigger("chosen:updated"); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // The UI modules load in parallel with metaApiPromise above (mediawiki.api // + the two API requests) — this block only needs to wait on them. // jquery.chosen is only pulled in if it is actually going to be used. var uiModules = ["oojs-ui-core"]; if (useChosen) uiModules.push("jquery.chosen"); mw.loader.using(uiModules, function () { // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). // data.json stores these as a plain {code: label} object. if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true); // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> 81cyi8kck4zfrg244eammdttp1cgo0d 6291628 6291627 2026-07-27T09:35:14Z Wikihez 7344 6291628 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 action = mw.config.get("wgAction"); var pageExists = !!mw.config.get("wgArticleId"); var addLangMode = pageExists && mw.util.getParamValue("wiktkuaddlang") === "1"; if (action === "view" && pageExists) { var targetContainer; var skin = mw.config.get('skin'); 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", "Ji vê rûpelê re ziman an jî beşeke nû zêde bike"); return; } if (!(action === "edit" || action === "submit")) { return; } if (!addLangMode && !$(".mw-newarticletext, .mw-newarticletextanon").length) { return; } mw.notify( "Amûr dest pê dike, hinek bisekinin.\nNot: jkx Sentaks (Syntax highlighting) neaktîv bikin", { type: "info", tag: "wiktku-start-notice", autoHide: true, autoHideSeconds: 10 } ); // Kick off both metadata API requests as early as possible, needing only // the mediawiki.api module rather than waiting for jquery.chosen and // oojs-ui-core too (those are only needed later, to actually build the // form). mediawiki.api is used all over the wiki, so this resolves // near-instantly in practice — but we still wait for it properly rather // than assuming mw.Api is already defined. The jquery.chosen/oojs-ui-core // load happens in parallel with this, further down. var dataJsonPromise, zimanJsonPromise; var metaApiPromise = mw.loader.using("mediawiki.api").then(function () { var metaApi = new mw.Api(); dataJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-start.js/data.json", rvprop: "content", rvslots: "main", formatversion: 2 }); zimanJsonPromise = metaApi.get({ action: "query", prop: "revisions", titles: "MediaWiki:Gadget-translation_editor.js/ziman.json", rvprop: "content", rvslots: "main", formatversion: 2 }); return dataJsonPromise; }); 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, "\\$&"); } // Regex source for a "== {{ziman|CODE}} ==" header, tolerant of extra // whitespace. codePattern: escaped code for one language, or "[^}]+" for any. function langHeaderSource(codePattern) { return "==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*=="; } // Finds a language's header in wikitext (see langHeaderSource). Returns // { index, match } or null. function findLangHeader(text, code) { var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text); return m ? { index: m.index, match: m[0] } : null; } function stripWikilinks(str) { return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) { return display || target; }); } 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; } // Members of [[Kategorî:Çavkanî]] (source/citation templates), used by the // "Ji Kategorî:Çavkanî şablonekê hilbijêre" jêder-source option. Fetched // once on first use and cached — every citation row's picker shares it. 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"); // Draft cache for this page — lets the editor manually save/restore the // whole wizard state (shared lang/pron + every section) via // [[MediaWiki:Gadget-StorageUtils.js]]'s ObjectStorage, independent of // any single browser session. Keyed per page so different articles' // drafts don't collide. var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1); // Mobile detection: MobileFrontend sets wgMFMode, or fall back to touch check. var isMobile = !!mw.config.get("wgMFMode") || window.matchMedia("(pointer: coarse)").matches; // ---- Searchable picker -------------------------------------------------- // Every dropdown in this form -- desktop and mobile alike -- uses the // wrapNativeSelect() widget further down. jquery.chosen used to fill this // role on desktop, but it renders search results in plain option order and // offers no hook for re-ranking them, so it could never put an exact code // match ("ang" -> "Îngilîziya kevn") above the substring matches. It is no // longer loaded or referenced anywhere. // ---- Diacritic-insensitive search -------------------------------------- // Lets "ingilizi" find "Îngilîzî", "kurdi" find "Kurdî", "sopani" // find "Şopanî". NFD decomposition handles every letter written as // base + combining accent (î ê û ç ş á é ö ...); // FOLD_EXTRA covers the handful Unicode cannot decompose that way. var FOLD_EXTRA = { "ı": "i", // dotless i "ø": "o", // o with stroke "đ": "d", // d with stroke "ð": "d", // eth "ə": "e", // schwa "ħ": "h", // h with stroke "ł": "l", // l with stroke "ŋ": "n", // eng "ʉ": "u", // u bar "ɨ": "i", // i bar "ʒ": "z", // ezh "ɛ": "e", // open e "ɔ": "o", // open 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; }); } /** * Replaces a native <select> with a searchable text input + ranked result * list. Returns a wrapper element containing the original (hidden) select, * so every call-site that reads .val() or listens for "change" on the * select keeps working unchanged. * * After changing the option list, trigger "wiktku:refresh" on the select so * the widget re-reads its <option>s. */ function wrapNativeSelect(select) { var wrapper = $("<span class='wiktku-native-select-wrap'></span>"); // Multi-selects aren't supported by this widget, and a native multiple // <select> is perfectly usable as-is -- leave it alone. if (select.prop("multiple")) { return wrapper.append(select); } var placeholder = select.attr("data-placeholder") || "Le bigere..."; // Hide the native select -- we drive it programmatically. 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(); // Full option cache -- rebuilt when wiktku:refresh fires. var allOptions = []; // Options currently rendered, in display order, plus the keyboard cursor. var shown = []; var activeIndex = -1; function cacheOptions() { allOptions = []; select.find("option").each(function () { var v = $(this).val(); if (!v) return; // skip the empty placeholder option var text = $(this).text(); // Language options read "Name (code)" -- score the name on its own // 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"), 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. This is the whole point of the widget: a // code that *is* the query, or begins with it, always outranks a name // that merely contains it -- so "ang" lists "Îngilîziya kevn (ang)" // first instead of burying it among every name containing "ang". 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 }); }); // Multi-word fallback (Chosen's enable_split_word_search): keep // anything whose label contains every token, e.g. "kevn ing". 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>") .text(opt.text) .attr("data-value", opt.value) .attr("aria-selected", "false"); // 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 () { setActive(idx); }); resultsList.append(li); }); positionList(); resultsList.show(); textInput.attr("aria-expanded", "true"); } function setActive(idx) { 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"); // Keep the cursor visible inside the scrollable list. 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 -- the behaviour Chosen had on desktop. 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); }); // wiktku:refresh -> re-cache the option list. Fired after ziman.json // loads, after options are enabled/disabled, and after programmatic // .val() changes. "chosen:updated" is kept as a legacy alias in case // another gadget still fires the old Chosen event at these selects. select.on("wiktku:refresh chosen:updated", 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. Selects * marked .wiktku-plain-select opt out, and the call is idempotent, so it is * safe to re-run after new rows or sense blocks are inserted. */ 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)); }); } var langsByCode = {}; // Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime. var metadata = {}; // Zaravayên kurdî yên ku di beşa "Bi zaravayên din" de tên pêşkêşkirin — // ev kod ji beşa Werger têne derxistin daku heman zimên du caran neyê xwendin. var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"]; // Converts a plain {code: label} object (the format used for langChoices // and posChoices in data.json) into the ordered array of choice objects // ({code, label[, sortKey]}) that the rest of this script works with. // isLang: true adds a sortKey (defaults to label) and tells populateMenus // to show "(code)" after the label — used for 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 = {}; // Sub-lists that only make sense for a plain (non-şablon) sense — hidden // when a şablon (e.g. {{guharto|…}}) is chosen for that sense, since a // template-based sense has no "own" gloss for alternate forms, synonyms, // antonyms, coordinate terms, hypernyms or hyponyms to attach to. Etîket, // Nimûne and Jêgirtin stay visible either way. 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; } // 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; } // 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#* {{bênimûne|" + lang + "}}"; } return $.map(nimunes, function (n) { var text = typeof n === "object" ? n.text : n; if (!text) return undefined; var bolded = text.replace( new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "ig"), "'''$1'''"); var tpl = "\n#: {{nm|" + lang + "|" + bolded; if (typeof n === "object") { if (n.tr) tpl += "|tr=" + n.tr; // Translation as third positional param (no key needed). if (n.t) tpl += "|" + 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 < 200 && 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; if (jeder.jederTpl) { var cite = "\n#* {{" + jeder.jederTpl + "|" + lang; cite += "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) cite += "|tr=" + jeder.quoteTr; if (jeder.quoteT) cite += "|tercime=" + jeder.quoteT; $.each(jeder.jêderFields || {}, function (key, val) { if (val) cite += "|" + key + "=" + val; }); cite += "}}"; return cite; } var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + jeder.quote; if (jeder.quoteTr) out += "|tr=" + jeder.quoteTr; if (jeder.quoteT) out += "|tercime=" + jeder.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); } function makeWordListStep(id, name, question_fmt, header, templateName) { return { id: id, name: name, question_fmt: question_fmt, multiple: true, narrow: true, visible: function (values) { return values.lang && values.pos; }, output: function (values) { var words = $.grep(values[id] || [], function (w) { return !!w; }); if (!words.length) return ""; var lang = values.lang; var section = "\n==== " + header + " ====\n"; if (words.length === 1) { // Single word — keep the simple inline template. return section + "* {{" + templateName + "|" + lang + "|" + words[0] + "}}\n"; } // Multiple words — use {{kol3}} with one positional param per word. 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" : "")); } }); // 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>"); 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.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 too, so it goes back to its // placeholder instead of keeping the inserted template name. sel.trigger("wiktku:refresh"); ta.focus(); }); wrap.append(sel).append($("<br>")).append(ta); stepDiv.append(wrap); initSelects(wrap); return ta; }, 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.charAt(0) === "*" ? line : "* " + line; }).join("\n"); 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 cinsiyet = values.cinsiyet || ""; // from the cinsiyet step var lekTur = values.lektur || ""; // from the lêker transitivity step var capitalizedPos = capitalize(pos); var outputString = "\n=== " + capitalizedPos + " ===\n"; // Add ku-tewîn-nav for single-word navdêr/serenav with known gender. if (!pageName.includes(" ") && cinsiyet && GENDER_BASE_LABELS[cinsiyet] && (pos === "navdêr" || pos === "serenav") && values.lang === "ku") { outputString += "{{ku-tewîn-nav|" + pageName + "|" + GENDER_BASE_LABELS[cinsiyet]; if (pos === "serenav") outputString += "|y"; outputString += "}}\n"; } outputString += "{{" + pos + "|" + values.lang; if (pos === "navdêr" || pos === "serenav") { if (cinsiyet) { outputString += "|c=" + cinsiyet; } else if (values.lang === "ku") { // Unknown gender — prompt editor to fill it in. outputString += "|c=?"; } } else if (/^lêker/.test(pos) && lekTur) { outputString += "|c=" + lekTur; } if (values.tr) { outputString += "|tr=" + values.tr; } outputString += "}}\n"; return outputString; } }, // 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 (values) { var meta = metadata[values.lang]; return !!(values.lang && (values.pos === "navdêr" || values.pos === "serenav") && meta && meta.g && meta.g.length); }, build: function (stepDiv, values) { var select = stepDiv.find("select.wiktku-start-step-field"); if (!select.length) { select = $(mw.html.element("select", { "class": "wiktku-start-step-field wiktku-plain-select", id: "wiktku-start-cinsiyet-field" })); stepDiv.append(select); } if (!values) return select; var meta = metadata[values.lang]; var codes = (meta && meta.g) || []; var prevVal = select.val(); var choices = $.map(codes, function (code) { return [{ code: code, label: genderLabel(code) }]; }); populateMenus(select, choices, null, "Cinsiyetê hilbijêre"); if ($.grep(choices, function (c) { return c.code === prevVal; }).length) { select.val(prevVal); } return select; }, output: function () { return ""; } // consumed by pos.output }, // Lêker transitivity — shown for any lêker variant. lektur: { id: "lektur", name: "Cureyê lêkerê (fiîlê)", question_fmt: "Cureyê lêkerê (fiîlê) çi ye?", question_br: true, visible: function (values) { return values.lang && values.pos && /^lêker/.test(values.pos); }, // Dictionary: value → display label. Add new types here. // NOTE: populateMenus() expects {code, label} objects (like every // other step's choices) — plain [code, label] pairs leave // choice.code/choice.label undefined, so Chosen (desktop) ends up // with unusable options. 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 ""; } // consumed by pos.output }, tr: { id: "tr", name: "transkrîpsiyon", question_fmt: "Transkrîpsiyona kelîmeyê", multiple: false, narrow: true, visible: function (values) { return values.lang && values.pos && needsTransliteration(values.lang); } }, dfn: { id: "dfn", // required: true, ger mane tinebe [[Şablon:mane?]] bi kar bîne name: "Mane", question_fmt: "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin! Ji bo tercimeyan beşa 'Werger' bi kar bîne!", 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); }); // — 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 — used by updateTplOptions // to remove/re-add options (the only approach compatible with Chosen.js). var _allTplOptions = []; var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" })); tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>")); $.each(tplDefs, function (name, tpl) { var opt = $("<option></option>") .val(name) .text(name) .attr("data-langs", tpl.langs ? tpl.langs.join(",") : "") .attr("data-nolang", tpl.noLang ? "1" : ""); tplSelect.append(opt); _allTplOptions.push(opt[0]); }); 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. // This is the only approach that works correctly with both native <select> // and Chosen.js on desktop. 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; } }); tplSelect.trigger("wiktku:refresh"); if (currentVal && !currentStillValid) { tplSelect.val(""); dfnArea.show(); tplTitleInput.hide().val(""); } } // 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ûpelê…") .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); }); // Ş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 programmatically setting tplSelect's value. block.data("updateFieldVisibility", updateFieldVisibility); tplSelect.on("change", function () { var chosen = $(this).val(); if (chosen) { dfnArea.hide(); tplTitleInput.show().focus(); } else { dfnArea.show(); tplTitleInput.hide().val(""); } updateFieldVisibility(); // Re-run refresh so the Werger step visibility updates. refresh(); }); var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row") .append(tplSelect).append(" ").append(tplTitleInput); block.append(tplRow); block.append(dfnArea); // Apply lang filter on initial build. updateTplOptions(); // — Sub-field groups — // All groups are always visible with a placeholder textarea. // Nimûne is last in the DOM and last in the output. var allGroups = [ { cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî" }, { cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike" }, { cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê" }, { 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-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" }, { cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e" }, { cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e" }, { cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false } ]; $.each(allGroups, function (_, g) { var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group"); 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 () { 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", "Transkrîpsiyona 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 kurmancî") ); } if (subWrap.children().length) li.append(subWrap); return li; } if (g.cls === "wiktku-dfn-nimune") { // Nimûne — compound widget with tr/t sub-fields. var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel").text(g.label + ": "); 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").text(g.label + ": " + g.izah); var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list"); 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", g.label + "…"); firstInput.on("input", function () { steps.dfn.onSubInput(list, this, g.rows, g.narrow); }); list.append($("<li></li>").append(firstInput)); groupDiv.append(lbl).append(list); block.append(groupDiv); } }); // — Jêder (citation) 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").text("Jêgirtin: "); 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"); var dragHandle = $("<span></span>") .addClass("wiktku-jeder-drag-handle") .attr("title", "Bişkûne û bikişîne da ku rêzê biguhezîne"); // 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 () { var val = $.trim($(this).val()); var isLast = li.is(jederList.children("li:last-child")); if (val && isLast) { jederList.append(buildJederLi()); } }); li.append(dragHandle).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", "Transkrîpsiyona 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 = $("<input type='text'>") .addClass("wiktku-dfn-quote-citoid-input wiktku-start-narrow") .attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…"); var citoidBtn = new OO.ui.ButtonWidget({ label: "Bi Citoid bîne", classes: [ "wiktku-dfn-quote-citoid-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.citoidHelper.ensure().then(function () { return 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(); if (!chosen) 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, // so the saved value is always exactly what's in that // (editable) textarea — no separate hidden field that can // end up empty and silently fall back to "çavkanî?". 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", "Navê şablonê lê 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…"); function showCatResults(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.). function selectCatTpl(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 + "…"); row.append(fieldLbl).append(inp); srcFields.append(row); }); }); li.append(citoidWrap).append(srcSel).append(srcFields); return li; } jederGroupDiv.data("buildJederLi", buildJederLi); jederList.append(buildJederLi()); // Make citation rows sortable (drag to reorder). // The last row is always the empty trailing row — cancel its drag. mw.loader.using("jquery.ui.sortable", function () { jederList.sortable({ handle: ".wiktku-jeder-drag-handle", axis: "y", items: "li:not(:last-child)", tolerance: "pointer" }); }); jederGroupDiv.append(jederLbl).append(jederList); block.append(jederGroupDiv); }()); // Apply the initial hide/show state for the sub-groups (matches // tplSelect's starting value, which is normally empty). updateFieldVisibility(); return block; }, /** Grows the sense list when the last mane textarea gets content. */ onDfnInput: function (stepDiv, block, textarea) { var val = $.trim($(textarea).val()); var list = stepDiv.find("ol.wiktku-dfn-list"); var isLast = block.is(list.children("li:last-child")); if (val && isLast) { var newBlock = steps.dfn.buildSenseBlock(stepDiv); list.append(newBlock); // Give the new block's template selector the searchable picker. initSelects(newBlock); } 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) { 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); }); list.append($("<li></li>").append(next)); } }, // 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; var list = $("<ol class='wiktku-dfn-list'></ol>"); list.append(steps.dfn.buildSenseBlock(stepDiv)); stepDiv.append(list); 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); }); // True only when at least one sense has a regular mane (needs linkify). var hasRegularMane = senses.some(function (s) { return !s.tpl && s.mane !== ""; }); // nimûne/jêder must never pass through linkify, only the mane text. // defLines (linkified) and citeLines (kept aside) stay parallel; // a {{wiktku-cite-N}} token in defLines marks where to splice back. var defLines = []; // one entry per kept sense (goes to linkify) var citeLines = []; // parallel — nimûne+jêder for that same sense $.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); return; } // — Normal mane sense — var dfn = sense.mane; if (!dfn && !senseHasExtraContent(sense)) return; // truly empty if (dfn) { dfn = stripWikilinks(dfn); if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn); if (!dfn.match(/[.…?!)\]]$/)) dfn += "."; } else { dfn = "{{mane?|" + lang + "}}"; } var katTpl = buildSenseTpl(sense, "kategori", lang, "K"); var labelTpl = buildSenseTpl(sense, "etiket", lang, "e"); var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn; 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); var idx = defLines.length; defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : "")); citeLines.push(cite); }); // 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)] || ""; }); } var text = defLines.join("\n") + "\n"; if (!hasMane) { // No definitions entered — emit {{mane?}} with bênimûne. text = "# {{mane?|" + lang + "}}\n#* {{bênimûne|" + lang + "}}\n"; callback(text); return; } if (!hasRegularMane) { // All filled senses are template senses — no linkify needed. callback(restoreCites(text)); return; } var didTryToLoad = function () { if (typeof $.wiktviLinkify === "function") { $.wiktviLinkify(text, function (linked) { callback(restoreCites(linked)); }); } else { callback(restoreCites(text)); } }; mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad); } }, je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse", "Jê", "g"), tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse", "Têkildar", "g"), binereherwiha: makeWordListStep("binereherwiha", "binêre herwiha", "Kelîmeyên din ên eleqedar binivîse", "Binêre herwiha", "g"), 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has 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", "Nimûne: 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($("<br>")).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, // Only when a regular sense has content (real mane, or {{mane?}} // with other fields filled in) — a şablon sense never counts. var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn")); var hasRegularMane = dfnSenses.some(function (s) { return !s.tpl && (s.mane !== "" || senseHasExtraContent(s)); }); if (!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: { "navz": 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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: "Eger ev kelîme di zaravayên din de bi awayekî cuda tê gotin an nivîsandin, wan lê zêde bike", visible: function (values) { return steps.trans.visible(values); }, disabledKeys: { "navz": true }, 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", "Zaravayekî lê bigere"); populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys, "Zarava hilbijêre"); 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 ""; 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) 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"); if (!wordLines) return undefined; var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}"; 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: "Wergera 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 — chosen is applied after insertion into DOM. var langSel = $(mw.html.element("select", { "class": "wiktku-start-step-keyfield wiktku-start-narrow" })); langSel.attr("data-placeholder", "Zimanekî lê 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 ""; // 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 — nothing to output, and gloss.text is // empty so there's no caption to give {{werger-ser}} anyway. if (!gloss.text && !rows.length) 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 ên (ne jêgirtinan) 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)); }); // Notify Chosen (desktop) / the search widget (mobile) that the // option list has 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 jêrê 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($("<br>")).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); // Populate only now: refreshTplSelect() looks the select up by id // under #wiktku-start-cavkani, so running it before the wrap was // appended found nothing and bailed out — which left the dropdown // empty on every beş after the first, once the category members // were already cached. 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); var ordered_steps = [ steps.lang, steps.pron, steps.pos, steps.cinsiyet, steps.lektur, steps.tr, steps.dfn, steps.je, steps.tekildar, steps.etym, steps.dunde, steps.binereherwiha, steps.zarava, steps.trans, steps.cavkani]; // Step ids that belong to each section (not shared across sections). var PER_SECTION_STEP_IDS = [ "pos", "cinsiyet", "lektur", "tr", "dfn", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani" ]; // ── 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; 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 list so build() recreates it cleanly. 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(); } else { block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show(); block.find("input.wiktku-dfn-tpl-title").hide().val(""); } // Re-sync the formên alternatîv/hevmane/… hide state now that // tplSelect's value was set programmatically (its own "change" // handler doesn't fire from .val(), so this doesn't happen otherwise). var updateVis = block.data("updateFieldVisibility"); if (updateVis) updateVis(); // 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 // 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); }); subList.append($("<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); 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); 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()); } }); } // 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; } 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(); } }); 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 === "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"); }); } function getWikitextForCurrentSection(callback) { var values = getValues(); var entry = steps.pos.output(values); steps.dfn.getOutput(values, function (output) { entry += output; 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); callback(entry, values); }); } function getWikitextAllSections(callback) { saveCurrentSection(); var results = new Array(sections.length); var sharedValues = getValues(); // lang/pron are shared function processSection(i) { 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); }); } processSection(0); } // Assembles wikitext for all sections plus the shared lang/pron header. function getWikitext(callback) { getWikitextAllSections(function (sectionTexts, sharedValues) { var entry = steps.lang.output(sharedValues); entry += steps.pron.output(sharedValues); entry += sectionTexts.join("\n"); // 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) { return s.mane; }).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; 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); nextInput.on("input", function (evt) { refreshForTextField(evt.target, step); }); var nextKeyInput = buildKeyInput(step); var nextItem = $("<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 didFocus = false; 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(); // 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", "Transkrîpsiyon…"); 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ê 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(); }); }()); // 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"); li.find(".wiktku-dfn-nimune-quote").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", "Transkrîpsiyona 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"); li.find(".wiktku-dfn-nimune-quote").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ê ya 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) { if (typeof(step.visible) === "function" && !step.visible(values)) { $("#wiktku-start-" + stepId).slideUp(); } else { var stepDiv = $("#wiktku-start-" + stepId); if (changedStep !== step && typeof(step.build) === "function") { prepareInput(step.build(stepDiv, values), step); } 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); } var didSlideDown; if (!didFocus && stepDiv.css("display") === "none" && step.required) { didSlideDown = function (evt) { input.focus(); }; didFocus = true; } stepDiv.slideDown(didSlideDown); } if (step.required && (values[stepId] === "" || (step.multiple && values[stepId].join("") === ""))) { canContinue = false; } }); continueBtn.setDisabled(!canContinue); } function destroy() { $("#wiktku-start-form, .noafterwizard").slideUp(function () { $(this).remove(); }); $(".noarticletext .nowizard, #editform").slideDown(); $("#ca-edit").removeClass("selected"); $("#ca-wiktku-nowizard").addClass("selected"); } // Priority order for Kurdish dialects and the navz pseudo-code. // 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 at all — just append. var sep = existing && !/\n\s*$/.test(existing) ? "\n\n" : ""; return existing + sep + 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 separated by newlines. var joined = sections.map(function (s) { // Ensure each section ends with exactly one newline before the next. return s.content.replace(/\n+$/, ""); }).join("\n"); return preamble + 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 di rûpelê de 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(" Naveroka rûpelê paqij bike (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>"); startForm.submit(function (evt) { evt.preventDefault(); continueBtn.setDisabled(true); spinner.show(); getWikitext(function (wikitext) { if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) { // User asked to wipe the page and start fresh — just // write the newly generated wikitext as the full content. $("#wpTextbox1").val(wikitext); } else if (addLangMode) { var textbox = $("#wpTextbox1"); var existing = textbox.val() || ""; // 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!"); $("#wiktku-preview-container").remove(); destroy(); }); }); $.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); stepDiv.append($(mw.html.element("h3", { "class": "wiktku-start-step-name" }, new mw.html.Raw(mw.html.element("label", { "for": "wiktku-start-" + step.id + "-field" }, name))))); if (step.question_fmt) { stepDiv.append($(mw.html.element("label", { "class": "wiktku-start-step-question", "for": "wiktku-start-" + step.id + "-field" }, step.question_fmt))); 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.multiple) { input.on("input", function (evt) { refreshForTextField(evt.target, step); }); var keyInput = buildKeyInput(step); var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>") .append($("<li></li>").append(keyInput).append(input)); stepDiv.append(list); } else stepDiv.append(input); } prepareInput(input, step); stepDiv.hide(); // Insert tab bar just before the first per-section step. if (step.id === "pos") startForm.append(sectionTabsDiv); 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ş zêde bike", disabled: true }); addSectionBtn.on("click", function () { saveCurrentSection(); sections.push({}); activeSection = sections.length - 1; $.each(PER_SECTION_STEP_IDS, function (_, id) { if (id === "dfn") { var stepDiv = $("#wiktku-start-dfn"); 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 === "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"); }); renderSectionTabs(); refresh(); $("html, body").animate({ scrollTop: sectionTabsDiv.offset().top - 20 }, 300); }); /** 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(); return; } sectionTabsDiv.show(); $.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); (function (idx) { tab.on("click", function () { if (idx === activeSection) return; saveCurrentSection(); activeSection = idx; restoreSection(sections[activeSection]); refresh(); renderSectionTabs(); }); }(i)); sectionTabsDiv.append(tab); }); // 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); } renderSectionTabs(); /** * Restores a full cached draft: shared lang/pron fields, then every * section (via the same restoreSection()/refresh()/renderSectionTabs() * sequence already used when switching tabs). * @param cached {{lang: string, pron: string, 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); } 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(); mw.notify("Danûstandina berê ya tomarkirî 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(); }); // 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 () { mw.notify("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 // which is invisible on mobile and easy to miss on desktop. var sourceEditorBtn = new OO.ui.ButtonWidget({ label: "Edîtora çavkaniyê veke" }); 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 (see the // ziman.json .then() callback below), once the full language list // is available. var saveCacheBtn = new OO.ui.ButtonWidget({ label: "Li cache tomar bike", title: "Rewşa niha ya vê formê (ziman, bilêvkirin û hemû beş) li cache (localStorage) tomar 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(); var snapshot = { lang: getValues().lang || "", pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "", sections: sections, activeSection: activeSection }; // 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 { formCache.Set(snapshot); mw.notify("Rewşa formê li cache hate tomarkirin.", { type: "success", tag: "wiktku-cache-save" }); } catch (e) { mw.notify("Tomarkirina li cache serneket.", { type: "error", tag: "wiktku-cache-save" }); } }); footer.append(saveCacheBtn.$element); var clearCacheBtn = new OO.ui.ButtonWidget({ label: "Cache paqij bike", flags: [ "destructive" ], title: "Daneyên tomarkirî yên vê rûpelê ji cache jê bibe (formeke ku niha xuya dike naguherîne)." }); clearCacheBtn.on("click", function () { formCache.Set({}); mw.notify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" }); }); footer.append(clearCacheBtn.$element); // ── Preview checkboxes ─────────────────────────────────────────────── // Wikitext preview (raw text panel) var previewCheck = $("<input type='checkbox' id='wiktku-preview-toggle'>"); var previewLabel = $("<label for='wiktku-preview-toggle'></label>") .text(" Pêşdîtina wîkîtekstê"); var previewPanel = $("<pre id='wiktku-preview-panel'></pre>").hide(); // Live rendered preview (parsed HTML panel, same logic as AjaxEdit). // Not available on mobile — API polling causes too many problems on small screens. var livePreviewCheck = null; var livePreviewPanel = null; if (!isMobile) { livePreviewCheck = $("<input type='checkbox' id='wiktku-live-preview-toggle'>"); livePreviewPanel = $("<div id='wiktku-live-preview-panel'></div>").hide(); } // Container that holds both panels and switches between stacked / side-by-side. var previewContainer = $("<div id='wiktku-preview-container'></div>"); previewContainer.append(previewPanel); if (!isMobile) previewContainer.append(livePreviewPanel); var previewTimer = null; var livePreviewIntervalId = -1; var livePreviewActive = false; // guards in-flight fetches after uncheck /** Rebuilds wikitext preview if its checkbox is checked. */ function schedulePreview() { if (!previewCheck.prop("checked")) return; clearTimeout(previewTimer); // Debounce: wait 400 ms after the last keystroke before rebuilding. previewTimer = setTimeout(function () { getWikitext(function (wikitext) { previewPanel.text(wikitext).show(); updatePanelLayout(); }); }, 400); } /** Fetches parsed HTML and populates the live preview panel. */ function loadLivePreview() { getWikitext(function (wikitext) { new mw.Api().parse(wikitext, { title: mw.config.get("wgPageName") }).done(function (html) { // Discard result if the user unchecked while the fetch was in flight. if (!livePreviewActive) return; livePreviewPanel.html(html).show(); updatePanelLayout(); }); }); } /** Adjusts panel widths depending on which previews are visible. */ function updatePanelLayout() { var wikitextVisible = previewCheck.prop("checked"); var liveVisible = livePreviewCheck && livePreviewCheck.prop("checked"); if (wikitextVisible && liveVisible) { // Side by side — each panel takes roughly half the available width. previewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); livePreviewPanel.css({ "flex": "1 1 45%", "min-width": "280px" }); } else { // Full width for whichever is visible — only touch panels that are // actually shown so that setting flex doesn't override display:none. if (wikitextVisible) previewPanel.css({ "flex": "1 1 100%", "min-width": "" }); if (liveVisible && livePreviewPanel) livePreviewPanel.css({ "flex": "1 1 100%", "min-width": "" }); } } previewCheck.on("change", function () { if ($(this).prop("checked")) { schedulePreview(); } else { clearTimeout(previewTimer); previewPanel.hide(); updatePanelLayout(); } }); if (!isMobile) { livePreviewCheck.on("change", function () { if ($(this).prop("checked")) { livePreviewActive = true; livePreviewPanel.show(); loadLivePreview(); livePreviewIntervalId = setInterval(loadLivePreview, 500); } else { livePreviewActive = false; clearInterval(livePreviewIntervalId); livePreviewIntervalId = -1; livePreviewPanel.hide().empty(); updatePanelLayout(); } }); } // Listen on all input/change events on the form and update both previews. startForm.on("input change", function () { schedulePreview(); }); footer.append($("<span style='margin-left:1em'></span>") .append(previewCheck).append(previewLabel)); if (!isMobile) { var livePreviewLabel = $("<label for='wiktku-live-preview-toggle'></label>") .text(" Pêşdîtina zindî"); footer.append($("<span style='margin-left:1em'></span>") .append(livePreviewCheck).append(livePreviewLabel)); } startForm.append(footer); $("#editform").hide().before(startForm); // Insert preview container (holds both panels) after the form. startForm.after(previewContainer); // 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 (or Chosen, if useNativePicker is turned off). initSelects(startForm); refresh(); // Zimanan ji MediaWiki:Gadget-translation_editor.js/ziman.json bar bike. // Kodên ku bi "-pro" diqedin û navên ku "zimanê destan" tê de heye // tên derxistin. Zimanên xardkirî li serî dimînin, yên din // bi alfabeyê li dû wan tên. // Pinned choices come from MediaWiki:Gadget-start.js/data.json (langChoices), // already loaded into steps.lang.choices before build() runs. var pinnedChoices = steps.lang.choices.slice(); var pinnedCodes = $.map(pinnedChoices, function (choice) { return choice.code; }); zimanJsonPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var zimanData; try { zimanData = JSON.parse(content); } catch (e) { zimanData = {}; } var extraChoices = []; $.each(zimanData, function (code, name) { // Filter: skip proto-language codes and legendary/reconstructed langs. if (/-pro$/i.test(code)) return; if (/ziman[eê]\s+destan/i.test(name)) return; // Skip codes already in the pinned list (they are handled above). if (pinnedCodes.indexOf(code) !== -1) return; // sortKey: bare name without leading "zimanê " for sorting. var sortKey = name.replace(/^ziman[eê]\s+/i, ""); var choice = { code: code, label: name, sortKey: sortKey }; extraChoices.push(choice); langsByCode[code] = choice; }); // Populate langsByCode for pinned choices too. $.each(pinnedChoices, function (i, choice) { // Ensure pinned choices have a sortKey. if (choice.sortKey === undefined) choice.sortKey = choice.label; langsByCode[choice.code] = choice; }); // Sort the extra choices alphabetically by sort key. extraChoices.sort(function (a, b) { return a.sortKey.localeCompare(b.sortKey, userLang); }); // Final list: pinned first, then the rest. var allChoices = pinnedChoices.concat(extraChoices); steps.lang.choices = allChoices; steps.trans.keys = allChoices; steps.dunde.keys = allChoices; steps.zarava.keys = getZaravaKeys(allChoices); // Replacing the option list only needs a wiktku:refresh so each // picker re-reads its <option>s. var langMenu = $("#wiktku-start-lang-field"); populateMenus(langMenu, steps.lang.choices); langMenu.trigger("wiktku:refresh"); var transMenus = $("#wiktku-start-trans .wiktku-start-step-keyfield"); populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans)); transMenus.trigger("wiktku:refresh"); var dundeMenus = $("#wiktku-start-dunde .wiktku-start-step-keyfield"); populateMenus(dundeMenus, steps.dunde.keys, getDisabledKeys(steps.dunde)); dundeMenus.trigger("wiktku:refresh"); var zaravaMenus = $("#wiktku-start-zarava .wiktku-start-step-keyfield"); populateMenus(zaravaMenus, steps.zarava.keys, getDisabledKeys(steps.zarava), "Zarava hilbijêre"); // Was missing entirely: without this the Zarava picker kept serving the // option list it cached before ziman.json replaced it. zaravaMenus.trigger("wiktku:refresh"); // Now that the full language list is loaded, silently restore any // draft the editor previously saved for this page (if the "Li // cache tomar bike" button was ever used here). var cachedDraft = formCache.Get(); if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) { restoreFromCache(cachedDraft); } }); } // oojs-ui-core loads in parallel with metaApiPromise above (mediawiki.api // + the two API requests) — this block only needs to wait on it. // jquery.chosen is deliberately not loaded: the form has one dropdown // implementation now (wrapNativeSelect). mw.loader.using(["oojs-ui-core"], function () { // Load metadata, langChoices, posChoices and tplDefs from the companion // data page so they can be updated without touching this script. // (Request was already fired via metaApiPromise above, in parallel // with this module load — we're just picking up the result here.) metaApiPromise.then(function (data) { var page = data.query.pages[0]; var content = page && page.revisions && page.revisions[0].slots.main.content; var d; try { d = JSON.parse(content); } catch (e) { d = {}; } // Populate metadata. if (d.metadata) $.extend(metadata, d.metadata); // Populate gender labels. if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels); // Populate lang choices (pinned list used by build() → ziman.json loader). // data.json stores these as a plain {code: label} object. if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true); // 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 (now populated) lang choices. steps.trans.keys = steps.lang.choices; steps.dunde.keys = steps.lang.choices; steps.zarava.keys = getZaravaKeys(steps.lang.choices); }).always(build); }); }); //</nowiki> gtlcxilkwgtrsxpgbkny45qamuw3la2 superqehreman 0 1325422 6291560 6260487 2026-07-26T13:20:52Z Wikihez 7344 /* Navdêr */ 6291560 wikitext text/x-wiki == {{ziman|ku}} == === Bilêvkirin === * {{ku-IPA}} * {{ku-kîte}} === Navdêr === {{ku-tewîn-nav|superqehreman|mê û nêr}} {{navdêr|ku|z=mn}} # Di [[edebiyat]]a zarokan û [[fantastîk]] de her [[karakter]]ê [[xeyalî]], [[axlebî]] bi [[qewet]]eke [[dersirûştî]], ê ku li dijî [[sûc]]an [[şer kirin|şer dike]]. #* {{jêder-malper|ku|jêgirtin=Birçî bihêle, bêav bihêle, dîl bigre paşê wek '''super qehreman''' wî ji çalê derxe.|nivîskar=Çayan Okudici|sernav=Bizina reş, qurta qurta avê û qertel|malper=gazeteduvar.com|url=https://www.gazeteduvar.com.tr/bizina-res-qurta-qurta-ave-u-qertel-haber-1629016|sal=2023}} #* {{jêder-malper|ku|jêgirtin="'''Super Qehremanê''' kurd Gênco" hat|nivîskar=Cemil Oguz|url=https://diyarname.com/news.php?Idx=25354|malper=diyarname.com|sal=2017|sernav='Super Qehremanê kurd Gênco' hat}} #* {{jêder-kovar|ku|jêgirtin=Di pîranîya Cîhana Batman û '''super-qehremanan''' da mirovên normal ne girîng în lê dîsa ev rêze-xêzefîlm vê hişmendîyê xera dike û di cîhana mirovên normal da cih dide wan jî.|kovar=Temaşe|sernav=Xêzefîlmê Xêzefîlman Batman|nivîskar=Mehmûd Beyto|url=https://www.kovaratemase.com/wp-content/uploads/2021/03/Kovara-Temase-3.pdf|sal=2021}} ==== Formên alternatîv ==== * {{hw|ku|super-qehreman}} ==== Etîmolojî ==== {{yekser|en|ku|superhero}} ji {{qertaf|en|super|hero|w2=qehreman|nekat=1}}; wekheva {{qertaf|ku|super|qehreman}}. ==== Werger ==== {{werger-ser}} * {{Z|de}}: {{W+|de|Superheld|n}} * {{Z|bg}}: {{W+|bg|супергеро́й|n}} * {{Z|cs}}: {{W-|cs|superhrdina|n}} * {{Z|zh}}: *: {{Z|cmn}}: {{W+|cmn|超級英雄|tr=chāojí yīngxióng}} * {{Z|da}}: {{W-|da|superhelt|n}} * {{Z|eo}}: {{W-|eo|superheroo}} * {{Z|et}}: {{W-|et|superkangelane}} * {{Z|fi}}: {{W+|fi|supersankari}} * {{Z|fr}}: {{W+|fr|super-héros|n}}, {{W+|fr|superhéros|n}} * {{Z|gl}}: {{W-|gl|superheroe|n}} * {{Z|ka}}: {{W-|ka|სუპერგმირი}} * {{Z|hi}}: {{W-|hi|सुपर हीरो|n}} * {{Z|nl}}: {{W+|nl|superheld|n}} * {{Z|en}}: {{W+|en|superhero}} * {{Z|ga}}: {{W-|ga|sárlaoch|n}} * {{Z|it}}: {{W+|it|supereroe|n}} * {{Z|is}}: {{W-|is|ofurhetja|m}} * {{Z|ja}}: {{W+|ja|スーパーヒーロー|tr=sūpāhīrō|sc=Jpan}} * {{Z|ca}}: {{W-|ca|superheroi|n}} * {{Z|ko}}: {{W-|ko|슈퍼히어로}} * {{Z|lv}}: {{W-|lv|supervaronis}} * {{Z|lt}}: {{W-|lt|superherojus}} * {{Z|lb}}: {{W-|lb|Superheld}} * {{Z|mk}}: {{W-|mk|суперхе́рој|n}} * {{Z|ms}}: {{W+|ms|adiwira}} * {{Z|mt}}: {{W-|mt|supereroj|n}} * {{Z|mr}}: {{W-|mr|सुपरहिरो|n}} * {{Z|hu}}: {{W+|hu|szuperhős}} * {{Z|mn}}: {{W-|mn|супер баатар}} * {{Z|pl}}: {{W+|pl|superbohater|n}} * {{Z|pt}}: {{W+|pt|super-herói}} * {{Z|kk}}: {{W-|kk|суперқаһарман}} * {{Z|ru}}: {{W+|ru|супергеро́й|n}} * {{Z|es}}: {{W+|es|superhéroe}} * {{Z|sw}}: {{W-|sw|shujaa wa ajabu|n}}, {{W-|sw|shujaa mkuu|n}} * {{Z|sv}}: {{W+|sv|superhjälte|g}} * {{Z|th}}: {{W-|th|ซูเปอร์ฮีโร่|tr=sūpexr̒ḥīrò}}, {{W-|th|ยอดมนุษย์}} * {{Z|tr}}: {{W-|tr|süper kahraman}} * {{Z|uk}}: {{W-|uk|супергеро́й|n}} * {{Z|vi}}: {{W-|vi|siêu anh hùng}} * {{Z|cy}}: {{W-|cy|siwpyr-arwr|n}}, {{W-|cy|siwpyr-arwres|m}} * {{Z|el}}: {{W+|el|υπερήρωας|n}} {{werger-bin}} :{{werger çavkanî|en|superhero|beş=Ev qismê Wergerê|oldid=83357988}} s30kvq53rlgka5k3fausa8jhcr1jcvs Şablon:arşîvkirin 10 1357236 6291613 6235760 2026-07-26T22:44:29Z Wikihez 7344 dark mode 6291613 wikitext text/x-wiki {| class="tablo-flip" |- |<div class="float-right-flip" style="font-size:85%; border:solid 1px var(--wikt-palette-lightgrey, #ccc); padding:0.2em; margin-right:0.5em; text-align:center; width:180px; color:var(--wikt-palette-black, #202122); background: linear-gradient(to bottom, var(--wikt-palette-paleblue, #f8f9fa) 0%, var(--wikt-palette-lightgrey, #c8d2dc) 100%);"> [[Wêne:Replacement_filing_cabinet.svg|50px|link=]]<br> {{hlist|class=inline |<div> {{{1}}} </div> }} |}<noinclude>{{belgekirin}}</noinclude> evpoqc52e922tbkuovvna011a5i7s06 Modul:quote 828 1357519 6291566 6291540 2026-07-26T14:39:05Z Wikihez 7344 6291566 Scribunto text/plain --[=[ This module contains functions to implement quote-* templates. Author: Benwing2; conversion into Lua of {{quote-meta/source}} template, written by Sgconlaw with some help from Erutuon and Benwing2. The main interface is quote_t(). Note that the source display is handled by source(), which reads both the arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter. ]=] local export = {} --[=[ FIXME: 1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only. The relevant code checks for a capitalized word; it should probably instead check for the actual month names or abbrevs. ]=] -- Named constants for all modules used, to make it easier to swap out sandbox versions. local check_isxn_module = "Module:check isxn" local debug_track_module = "Module:debug/track" local en_utilities_module = "Module:en-utilities" local italics_module = "Module:italics" local labels_module = "Module:labels" local languages_module = "Module:languages" local languages_error_module = "Module:languages/error" local links_module = "Module:links" local number_utilities_module = "Module:number utilities" local parameters_module = "Module:parameters" local parse_utilities_module = "Module:parse utilities" local qualifier_module = "Module:qualifier" local roman_numerals_module = "Module:roman numerals" local scribunto_module = "Module:Scribunto" local script_utilities_module = "Module:script utilities" local scripts_module = "Module:scripts" local string_pattern_escape_module = "Module:string/patternEscape" local string_replacement_escape_module = "Module:string/replacementEscape" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local template_parser_module = "Module:template parser" local usex_module = "Module:usex/ceribandin" local usex_templates_module = "Module:usex/templates" local utilities_module = "Module:utilities" local yesno_module = "Module:yesno" local concat = table.concat local insert = table.insert local new_title = mw.title.new local remove = table.remove local require = require local sort = table.sort local u = mw.ustring.char local ugsub = mw.ustring.gsub local umatch = mw.ustring.match local unpack = unpack or table.unpack -- Lua 5.2 compatibility -- Use HTML entities here to avoid parsing issues (esp. with brackets) local SEMICOLON_SPACE = "&#59; " local SPACE_LBRAC = " &#91;" local RBRAC = "&#93;" local TEMP_LT = u(0xFFF1) local TEMP_GT = u(0xFFF2) local TEMP_LBRAC = u(0xFFF3) local TEMP_RBRAC = u(0xFFF4) local TEMP_SEMICOLON = u(0xFFF5) local function apply_subst(...) apply_subst = require(usex_module).apply_subst return apply_subst(...) end local function check_isbn(...) check_isbn = require(check_isxn_module).check_isbn return check_isbn(...) end local function check_issn(...) check_issn = require(check_isxn_module).check_issn return check_issn(...) end local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function embedded_language_links(...) embedded_language_links = require(links_module).embedded_language_links return embedded_language_links(...) end local function escape_wikicode(...) escape_wikicode = require(parse_utilities_module).escape_wikicode return escape_wikicode(...) end local function find_best_script_without_lang(...) find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang return find_best_script_without_lang(...) end local function find_parameters(...) find_parameters = require(template_parser_module).find_parameters return find_parameters(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function format_processed_labels(...) format_processed_labels = require(labels_module).format_processed_labels return format_processed_labels(...) end local function format_qualifier(...) format_qualifier = require(qualifier_module).format_qualifier return format_qualifier(...) end local function format_usex(...) format_usex = require(usex_module).format_usex return format_usex(...) end local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end local function get_number(...) get_number = require(number_utilities_module).get_number return get_number(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function gsplit(...) gsplit = require(string_utilities_module).gsplit return gsplit(...) end local function page_should_be_ignored(...) page_should_be_ignored = require(usex_templates_module).page_should_be_ignored return page_should_be_ignored(...) end local function parse_inline_modifiers(...) parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers return parse_inline_modifiers(...) end local function parse_inline_modifiers_from_segments(...) parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments return parse_inline_modifiers_from_segments(...) end local function parse_multi_delimiter_balanced_segment_run(...) parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run return parse_multi_delimiter_balanced_segment_run(...) end local function parse_term_with_lang(...) parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang return parse_term_with_lang(...) end local function pattern_escape(...) pattern_escape = require(string_pattern_escape_module) return pattern_escape(...) end local function pluralize(...) pluralize = require(en_utilities_module).pluralize return pluralize(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) end local function remove_links(...) remove_links = require(links_module).remove_links return remove_links(...) end local function roman_to_arabic(...) roman_to_arabic = require(roman_numerals_module).roman_to_arabic return roman_to_arabic(...) end local function replacement_escape(...) replacement_escape = require(string_replacement_escape_module) return replacement_escape(...) end local function scribunto_parameter_key(...) scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key return scribunto_parameter_key(...) end local function serial_comma_join(...) serial_comma_join = require(table_module).serialCommaJoin return serial_comma_join(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function split_alternating_runs(...) split_alternating_runs = require(parse_utilities_module).split_alternating_runs return split_alternating_runs(...) end local function split_and_process_raw_labels(...) split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels return split_and_process_raw_labels(...) end local function split_on_comma(...) split_on_comma = require(parse_utilities_module).split_on_comma return split_on_comma(...) end local function tag_text(...) tag_text = require(script_utilities_module).tag_text return tag_text(...) end local function tag_transcription(...) tag_transcription = require(script_utilities_module).tag_transcription return tag_transcription(...) end local function tag_translit(...) tag_translit = require(script_utilities_module).tag_translit return tag_translit(...) end local function ulen(...) ulen = require(string_utilities_module).len return ulen(...) end local function unitalicize_brackets(...) unitalicize_brackets = require(italics_module).unitalicize_brackets return unitalicize_brackets(...) end local function upper(...) upper = require(string_utilities_module).upper return upper(...) end local function usub(...) usub = require(string_utilities_module).sub return usub(...) end local function yesno(...) yesno = require(yesno_module) return yesno(...) end local function track(page) debug_track("quote/" .. page) end local function maintenance_line(text) return '<span class="maintenance-line">(' .. text .. ")</span>" end local function isbn(text) return "[[Special:BookSources/" .. text .. "|→ISBN]]" .. check_isbn( text, '&nbsp;<span class="error" style="font-size:88%">Invalid&nbsp;ISBN</span>[[Category:Pages with ISBN errors]]' ) end local function issn(text) return "[https://www.worldcat.org/issn/" .. text .. " →ISSN]" .. check_issn( text, '&nbsp;<span class="error" style="font-size:88%">Invalid&nbsp;ISSN</span>[[Category:Pages with ISSN errors]]' ) end local function lccn(text) text = text:gsub(" ", "") if text:find("-") then -- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]] local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$") if prefix then if ulen(part2) < 6 then part2 = ("0"):rep(6 - ulen(part2)) .. part2 end text = prefix .. part1 .. part2 end end return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]" end local function format_date(text) return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy") end -- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a -- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs. local function parse_and_format_labels(raw_lb, lang) if not raw_lb then return nil end local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true} labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true} if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances return {labels} end end -- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the -- name of the parameter from which the list of language codes was fetched. local function format_langs(langs) local names = {} for i, lang in ipairs(langs) do names[i] = lang:getCanonicalName() end if #names == 1 then return names[1] end return serial_comma_join(names) end local function get_first_lang(langs) return langs[1] or get_lang("und") end --[=[ Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters, plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it. The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers). Also exclude things that look like URL's from being parsed as having language code prefixes. ]=] local function val_should_not_be_parsed_for_annotations(val) return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://") end local param_mods = { t = { -- <t:...> and <gloss:...> are aliases. item_dest = "gloss", }, gloss = {}, -- Kurmanji aliases of <t:...>/<gloss:...>. w = { item_dest = "gloss", }, werger = { item_dest = "gloss", }, tercime = { item_dest = "gloss", }, alt = {}, -- Kurmanji alias of <alt:...>. cuda = { item_dest = "alt", }, tr = {}, ts = {}, subst = {}, sc = {type = "script"}, f = { convert = function(arg, parse_err) local prefix, val = arg:match("^(.-):([^ ].*)$") if not prefix then prefix = "" val = arg end local tags, sc_code, sc = prefix:match("^(.*)/(.-)$") if sc_code then sc = get_script(sc_code) or require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang") else tags = prefix end local quals if tags ~= "" then quals = split_on_comma(tags) for i, qual in ipairs(quals) do local obj = get_lang(qual, nil, "allow etym") or get_script(qual) quals[i] = obj or qual end end return { quals = quals, sc = sc, val = val, } end, store = "insert", }, q = {}, qq = {}, } local function generate_obj_annotated_text(text, parse_err, paramname) local obj = {} if text:find(":[^ ]") or text:find("%[%[") then local display, is_wikipedia_wikisource obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource = parse_term_with_lang { term = text, parse_err = parse_err, paramname = paramname } -- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link -- to avoid the w: or s: prefix showing. We should probably avoid this by calling -- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff -- that we currently do; but we need to test that carefully to make sure it doesn't do things like -- munge % signs. if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then -- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in -- parse_inline_modifiers(). obj.display = display end else obj.text = text obj.link = text end return obj end --[=[ Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...> inline modifier. If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g. 'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following fields: `lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is given. `text`: The text after stripping off any language prefix and inline modifiers. `link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`. `alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil. `subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter. `sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil. `tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil. `ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil. `gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise the <t:...> or <gloss:...> modifiers if given, otherwise nil. `f`: Foreign versions of the text. `q`: Left qualifiers. `qq`: Right qualifiers. Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of the text and CSS-tag the text accordingly, but to leave the text untagged. This object can be passed to format_annotated_text() to format a string displaying the text (appropriately script-tagged, unless `noscript` is set, as described above) and modifiers. ]=] local function parse_annotated_text(val, fullname, explicit_gloss) if not val then return nil end -- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused -- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having -- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a -- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script -- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big -- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the -- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even -- more.) if val_should_not_be_parsed_for_annotations(val) then return {text = val, link = val, noscript = true, gloss = explicit_gloss} end local obj if val:find("<") then -- Check for inline modifier. obj = parse_inline_modifiers(val, { paramname = fullname, param_mods = param_mods, generate_obj = generate_obj_annotated_text, }) else obj = generate_obj_annotated_text(val, nil, fullname) end if explicit_gloss then obj.gloss = explicit_gloss end return obj end local html_entity_char_to_replacement = { ["<"] = TEMP_LT, [">"] = TEMP_GT, ["["] = TEMP_LBRAC, ["]"] = TEMP_RBRAC, } local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code) -- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like) -- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get -- interpreted as a delimiter. local ch = decode_entities(entity) if ch ~= entity then return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON end -- If the entity doesn't decode, escape it anyway iff it follows a valid format. if hash == "" then -- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as -- invalid. return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity elseif x == "" then return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity end return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity end local html_entity_replacement_to_char = { [TEMP_LT] = "&lt;", [TEMP_GT] = "&gt;", [TEMP_LBRAC] = "&#91;", [TEMP_RBRAC] = "&#93;", [TEMP_SEMICOLON] = ";", } local function undo_html_entity_replacement(txt) -- Pattern covers everything in html_entity_replacement_to_char. return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char)) end -- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]] -- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no -- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present) -- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate -- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter); -- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a -- delimiter); and (c) colons not followed by a space (which may indicate a language prefix). local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement) local obj = generate_obj_annotated_text(text, parse_err, paramname) if not no_undo_html_entity_replacement then obj.text = undo_html_entity_replacement(obj.text) obj.link = undo_html_entity_replacement(obj.link) end return obj end --[=[ Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with their own inline modifiers. Some examples: * mainauthor=Paula Pattengale; Terea Sonsthagen * author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson * author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank * author=G Ristori; et al. * author=Jason Scott; zh:王晰宁<t:Wang Xining> * editors=zh:包文俊; zh:金心雯 * quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen> There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.: * author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}] * author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}}) * author=James (the Elder;) Humphrys There may also be HTML entities with semicolons in them: * author=&#91;{{w|Gilbert Clerke}}&#93; * 2ndauthor=Martin Biddle &amp; Sally Badham * author=Peter Christen Asbj&oslash;rnsen There may be both embedded semicolons and HTML entities with semicolons in them: * author=&#91;{{w|Voltaire}} [pseudonym; François-Marie Arouet]&#93; In general we want to treat &#91; like an opening bracket and &#93; like a closing bracket. Beware that they may be mismatched: * author=Anonymous &#91;{{w|Karl Maria Kertbeny}}] Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...> inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If `explicit_gloss` is specified and multiple values were seen, an error results.) Return value is a list of objects of the same sort as returned by parse_annotated_text(). ]=] local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname) if not val then return nil end -- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated -- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or -- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML -- entities; in the places where this occurs, the variable name contains 'html' in it. local splitchar, english_delim if val:find("^,") then splitchar = "," english_delim = "comma" val = val:gsub("^,", "") else splitchar = ";" english_delim = "semicolon" end -- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers). if not val:find("[<" .. splitchar .. "]") then if val_should_not_be_parsed_for_annotations(val) then return {{text = val, link = val, noscript = true}} else return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")} end end -- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or -- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand -- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a -- delimiter). if not val:find("[<>%[%](){}&]") then local entity_objs = {} for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do if val_should_not_be_parsed_for_annotations(entity) then insert(entity_objs, { text = entity, link = entity, noscript = true }) else insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement")) end end return entity_objs end -- Escape HTML entities, and get rid of directionality markers. local amp = val:find("&", nil, true) if amp then -- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities -- that have non-ASCII characters in their codes. val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement) end -- Pattern covers left-to-right (U+200E) and right-to-left (U+200F). val = val:gsub("\226\128[\142\143]", "") -- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket -- as matching literal versions of the same characters. local entity_runs = parse_multi_delimiter_balanced_segment_run( val, {{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}}, true ) if type(entity_runs) == "string" then local undo_val = undo_html_entity_replacement(val) -- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off -- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears -- the text is already script-tagged). return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}} end -- Split on semicolon (or comma), possibly surrounded by whitespace. local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*") -- Process each value. local entity_objs = {} for _, entity_group in ipairs(separated_groups) do -- Rejoin runs that don't involve <...>. local j = 2 while j <= #entity_group do if not entity_group[j]:find("^<.*>$") then entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1] remove(entity_group, j) remove(entity_group, j) else j = j + 2 end end local oneval = undo_html_entity_replacement(concat(entity_group)) -- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, -- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed -- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more. if val_should_not_be_parsed_for_annotations(oneval) then insert(entity_objs, { text = oneval, link = oneval, noscript = true }) else local obj if #entity_group > 1 then -- Check for inline modifier. obj = parse_inline_modifiers_from_segments({ group = entity_group, arg = oneval, props = { paramname = fullname, param_mods = param_mods, generate_obj = generate_obj_multivalued_annotated_text, }, }) else obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname) end insert(entity_objs, obj) end end if explicit_gloss then if #entity_objs > 1 then error( ( "Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> " .. "inline modifier attached to the individual entities" ):format(explicit_gloss_fullname, english_delim, fullname) ) end entity_objs[1].gloss = explicit_gloss end return entity_objs end --[=[ Format a text property that may be in a foreign language or script, along with annotations. This is conceptually similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is more appropriate for bibliographic entries. The output looks like this: TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS] `textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a similar function for the gloss. ]=] local function format_annotated_text(textobj, tag_text_func, tag_gloss_func) if not textobj then return nil end local text, link = textobj.text, textobj.link local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss -- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a -- Wikipedia link is given. local alt = textobj.alt or textobj.display if alt then if link:find("%[%[") or link:find("%]%]") then local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt) error(escape_wikicode(errmsg)) end text = ("[[%s|%s]]"):format(link, alt) end -- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}. -- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in -- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language -- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration. if not textobj.noscript then local lang = textobj.lang -- As an optimization, don't do script detection on an argument that contains only ASCII. local sc = textobj.sc or lang and lang:findBestScript(text) or not text:find("^[ -~]$") and find_best_script_without_lang(text) or nil -- As an optimization, don't do any of the following if there's no language, script, translit or transcription, -- as will be the case with simple ASCII values. if lang or sc or tr or ts then if not lang then lang = get_lang("und") end if tr == "-" then tr = nil elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn -- might return nil local text_for_tr = text if subst then text_for_tr = apply_subst(text_for_tr, subst) else text_for_tr = remove_links(text) end tr = (lang:transliterate(text_for_tr, sc)) end if text:find("%[%[") then -- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents, -- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this -- should not happen, and embedded_language_links() should do nothing if no embedded links -- are present. To work around this, only call embedded_language_links() when there are -- embedded links present. text = embedded_language_links({ term = text, lang = lang, sc = sc, }) end if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then text = tag_text(text, lang, sc) end if tr then -- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not -- likely to be a term that has an entry. tr = tag_translit(tr, lang, "usex") end if ts then ts = tag_transcription(ts, lang, "usex") end end end text = unitalicize_brackets(text) if tag_text_func then text = tag_text_func(text) end local parts = {} if textobj.q then insert(parts, format_qualifier(textobj.q) .. " ") end insert(parts, text) if tr or ts or f or gloss then insert(parts, SPACE_LBRAC) local subparts = {} if tr or ts then local tr_ts if ts then ts = "/" .. ts .. "/" end if tr and ts then tr_ts = tr .. " " .. ts else tr_ts = tr or ts end insert(subparts, tr_ts) end if f then for _, ff in ipairs(f) do local sc = ff.sc local lang if not sc and ff.quals then local qual = ff.quals[1] if type(qual) == "string" then -- do nothing; we'll do script detection farther down elseif qual:hasType("script") then sc = qual else -- language sc = qual:findBestScript(ff.val) lang = qual end end if not lang then lang = get_lang("und") end sc = sc or find_best_script_without_lang(ff.val) local val = embedded_language_links({ term = ff.val, lang = lang, sc = sc, }) if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then val = tag_text(val, lang, sc) end local qual_prefix if ff.quals then for i, qual in ipairs(ff.quals) do if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then ff.quals[i] = qual:getCanonicalName() end end qual_prefix = concat(ff.quals, "/") .. ": " else qual_prefix = "" end insert(subparts, qual_prefix .. val) end end if gloss then gloss = '<span class="e-translation">' .. gloss .. "</span>" gloss = unitalicize_brackets(gloss) if tag_gloss_func then gloss = tag_gloss_func(gloss) end insert(subparts, gloss) end insert(parts, concat(subparts, ", ")) insert(parts, RBRAC) end if textobj.qq then insert(parts, " " .. format_qualifier(textobj.qq)) end return concat(parts) end --[=[ Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(), joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using serial_comma_join() with the specified conjunction. `textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in format_annotated_text(). ]=] local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func) if not textobjs then return nil end if #textobjs == 1 then return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func) end local parts = {} for _, textobj in ipairs(textobjs) do insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func)) end -- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.") local use_semicolon = false for _, obj in ipairs(textobjs) do -- Use remove_links to ignore commas in links if obj.text and remove_links(obj.text):find(", ") then use_semicolon = true break end end local n = #parts if n > 0 and parts[n]:match("^'*et al[.']*$") then -- Special handling for 'et al.' parts[n] = "''et al.''" if n == 2 then -- author et al. return concat(parts, " ") else -- author 1, author 2, et al. return concat(parts, (use_semicolon and "; ") or ", ") end if delimiter == "and" or delimiter == "or" then delimiter = ", " end return concat(parts, delimiter) end if delimiter == "and" or delimiter == "or" then return serial_comma_join(parts, {conj = delimiter}) end return concat(parts, delimiter or ((use_semicolon and "; ") or ", ")) end -- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space. local function ine(arg) if not arg then return nil elseif type(arg) ~= "string" then return arg end arg = mw.text.trim(arg) if arg == "" then return nil end return arg end local abbrs = { ["a."] = {anchor = "a.", full = "ante"}, ["c."] = {anchor = "c.", full = "circa"}, ["p."] = {anchor = "p.", full = "post"}, } -- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec. -- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found, -- returns an empty string and the full date. local function process_ante_circa_post(date) local prefix = usub(date, 1, 2) local abbr = abbrs[prefix] local abbr_prefix = "" if abbr then abbr_prefix = "''[[Appendix:Glossary#" .. abbr.anchor .. '|<abbr title="' .. abbr.full .. '">' .. abbr.anchor .. "</abbr>]]'' " -- Remove lowercase letter, period, and space from beginning of date parameter. date = ugsub(date, "^%l%.%s*", "") end return abbr_prefix, date end -- Format the arguments that specify the date of the quotation. These include the following: -- |date=: The date. If |start_date= is given, this is the end date. -- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given. -- |start_date=: The start date, to specify a range. -- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given. -- |accessdate=: Date a website was accessed; processed if no other date was given. -- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date. -- -- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if -- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=, -- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is -- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of -- the range is |start_date_published=, etc. -- -- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in -- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to -- have an index added to them (|date2=, |year2=, |month2=, etc.). -- -- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used. -- -- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value. -- -- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e. -- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and -- neither |nodate= is given to indicate that there is no date, or |accessdate= is given). -- -- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category -- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added -- (in which case the scond return value will always be nil). local function format_date_args( a, get_full_paramname, alias_map, parampref, paramsuf, bold_year, maintenance_line_no_date, year_last ) local output = {} parampref = parampref or "" paramsuf = paramsuf or "" local function getp(param) return a(parampref .. param .. paramsuf) end local function pname(param) local fullname = get_full_paramname(parampref .. param .. paramsuf) return alias_map[fullname] or fullname end -- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of -- the parameter from which the timestamp was fetched, for error messages. local function format_date_with_code(code, timestamp, param) local language = mw.getContentLanguage() local ok, date = pcall(language.formatDate, language, code, timestamp) if ok then return date else -- All the formats used in format_date_args() are fine, so the timestamp must be at fault. error( ( "Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the " .. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]" ):format(pname(param), tostring(timestamp)) ) end end -- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following -- algorithm: -- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are -- given, the day shows up as 1. -- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given. -- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized -- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not. -- -- `param` is the base name of the parameter from which the timestamp was fetched. local function date_has_day_specified(timestamp, param) local day = format_date_with_code("j", timestamp, param) if day ~= "1" then return true end local english_month = timestamp:find("%u") local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " "))) local seen_nums = split(canon_timestamp, " ", true) local saw_one = false for _, num in ipairs(seen_nums) do if num == "1" or num == "01" then saw_one = true break end end if not saw_one then return false end return #seen_nums >= 3 or english_month and #seen_nums >= 2 end -- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include -- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param -- from which the date was fetched, for error messages. local function format_bold_date(date, explicit_day_given, date_param) local day_month_code = explicit_day_given and "j F" or "F" local month_day_code = explicit_day_given and "F j" or "F" if year_last then if bold_year then -- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given). return format_date_with_code(day_month_code .. " '''Y'''", date, date_param) else -- This formats like "3 August 2023" (or "August 2023" if day not explicitly given). return format_date_with_code(day_month_code .. " Y", date, date_param) end else if bold_year then -- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given). return format_date_with_code("'''Y''' " .. month_day_code, date, date_param) else -- This formats like "2023 August 3" (or "2023 August" if day not explicitly given). return format_date_with_code("Y " .. month_day_code, date, date_param) end end end -- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't -- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives -- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for -- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which -- uses {{#formatdate:}}) accept similar date formats. See: -- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]] -- * [[mw:Help:Extension:ParserFunctions##time]] -- * [[mw:Help:Magic_words#Formatting]] -- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date"; -- `parampref` is the prefix added to "date" to get the parameter name. local function fix_date(date, param_pref) if tonumber(date) ~= nil then error( ("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format( pname(param_pref .. "date"), pname(param_pref .. "year") ) ) elseif date and date:find("%s*%a+,%s*%d+%s*$") then error( ("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format( pname(param_pref .. "date"), pname(param_pref .. "month"), pname(param_pref .. "year") ) ) end if date then -- Commas are replaced with spaces to prevent parsing issues local fixed_date = ugsub(date, ", *", " ") return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1")) end end local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "") local year = getp("year") local month = getp("month") local start_year = getp("start_year") local start_month = getp("start_month") if date and year then error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year"))) end if date and month then error( ("|%s= should only be specified in conjunction with |%s=, not with |%s="):format( pname("month"), pname("year"), pname("date") ) ) end if start_date and start_year then error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year"))) end if start_date and start_month then error( ("|%s= should only be specified in conjunction with |%s=, not with |%s="):format( pname("start_month"), pname("start_year"), pname("start_date") ) ) end if (start_date or start_year) and not (date or year) then error( ("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format( pname("start_date"), pname("start_year"), pname("start_month"), pname("date"), pname("year"), pname("month") ) ) end local dash = "&nbsp;– " local day_explicitly_given = date and date_has_day_specified(date, "date") local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date") -- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or -- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based -- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already. -- `date_param` is the base name of the param from which the date was fetched, for error messages. local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param) if date then return format_bold_date(date, explicit_day_given, date_param) else -- Boldface a year spec if it's not already boldface. if bold_year and not yearobj.text:find("'''") then -- Clone the year object before modifying it because we may use it later to check against the current -- year (if we're dealing with start_year). yearobj = shallow_copy(yearobj) yearobj.text = "'''" .. yearobj.text .. "'''" if yearobj.alt then yearobj.alt = "'''" .. yearobj.alt .. "'''" end end if year_last then return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj) else return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "") end end end local yearobj = parse_annotated_text(year, pname("year")) local monthobj = parse_annotated_text(month, pname("month")) local start_yearobj = parse_annotated_text(start_year, pname("start_year")) local start_monthobj = parse_annotated_text(start_month, pname("start_month")) if start_yearobj then local abbr_prefix abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text) start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link)) insert(output, abbr_prefix) elseif yearobj then local abbr_prefix abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text) yearobj.link = select(2, process_ante_circa_post(yearobj.link)) insert(output, abbr_prefix) end if start_date or start_year then local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date") local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date") local beg_month = start_monthobj and start_monthobj.text or start_date and format_date_with_code("F", start_date, "start_date") or nil local beg_day = start_date and start_day_explicitly_given and format_date_with_code("j", start_date, "start_date") or nil if year_last then if beg_day then insert(output, beg_day .. " " .. beg_month) else insert(output, beg_month) end if beg_year ~= cur_year then insert(output, " " .. beg_year) end else insert(output, format_date_or_year_month( start_date, start_yearobj, start_monthobj, start_day_explicitly_given, "start_date" )) end if cur_year ~= beg_year then -- Different years; insert current date in full. if beg_month or cur_month then insert(output, dash) else insert(output, "–") end insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date")) elseif cur_month and cur_month ~= beg_month then local month_ins = monthobj and format_annotated_text(monthobj) or cur_month -- Same year but different months; insert current month and (if available) current day. if cur_day then insert(output, dash) if year_last then insert(output, cur_day .. " " .. month_ins) else insert(output, month_ins .. " " .. cur_day) end else if beg_day then insert(output, dash) else insert(output, "–") end insert(output, month_ins) end elseif cur_day and cur_day ~= beg_day then -- Same year and month but different days; insert current day. insert(output, "–") insert(output, cur_day) else -- Same year, month and day; or same year and month, and day not available; or same year, and month and -- day not available. Do nothing. FIXME: Should we throw an error? end if year_last and beg_year == cur_year then if cur_month then insert(output, " " .. cur_year) else insert(output, " " .. cur_year) end end elseif date or yearobj then insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date")) elseif not maintenance_line_no_date then -- Not main quote date. Return nil, caller will handle. return nil, nil elseif not getp("nodate") then local accessdate = getp("accessdate") if accessdate then local explicit_day_given = date_has_day_specified(accessdate, "accessdate") insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)") else if mw.title.getCurrentTitle().namespace ~= 10 then return maintenance_line(maintenance_line_no_date), true end return nil, nil end end return ine(concat(output)), nil end local function tag_with_cite(txt) return "<cite>" .. txt .. "</cite>" end -- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this -- module (formerly contained in {{quote-meta/source}}). function export.source(args, alias_map, format_as_cite, other_controls) local tracking_categories = {} local argslang = args[1] or args.lang if not argslang then -- For the moment, only trigger an error on mainspace pages and -- other pages that are not user pages or pages containing discussions. -- These are the same pages that appear in the appropriate tracking -- categories. User and discussion pages have not generally been -- fixed up to include a language code and so it's more helpful -- to use a maintenance line than signal an error. local current_title = mw.title.getCurrentTitle() if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then require(languages_error_module)(nil, 1) end end -- Given a canonical param, convert it to the original parameter specified by the user (which may have been an -- alias). local function alias(param) return alias_map[param] or param end -- Wrap contributor names (authors, editors, translators, compilers, etc.) and the bolded publication year in a -- span so they can be styled distinctly (e.g. Kurdish Wiktionary's "j-pewist" highlighting). local function j_lazim(text) if not text then return nil end return '<span class="j-pewist">' .. text .. "</span>" end local output, sep = {} local overrides = other_controls and other_controls.overrides or {} -- Add text to the output. The text goes into a list, and we concatenate all the list components together at the -- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that -- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and -- there's no title and no URL, then the first time we add anything after the title, we don't want to add a -- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or -- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator -- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting -- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text. local function add(text) if sep then insert(output, sep) end insert(output, text) sep = nil end local function add_with_sep(text, next_sep) add(text) sep = next_sep or ", " end -- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient. -- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last"). -- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=, -- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.). local function make_get_full_paramname(ind) return function(param) return param .. ind end end -- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above). -- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have -- wrapper functions that access params and define them only once. local get_full_paramname -- Return two values: the value of a parameter given the base param name (which may have a numeric index added), -- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch -- the parameter value from args[] given this name). The base parameter can be a list of such base params, which -- are checked in turn, or nil, in which case nil is returned. local function a_with_name(param) if not param then return nil elseif type(param) ~= "table" then local fullname = get_full_paramname(param) return args[fullname], alias(fullname) end for _, par in ipairs(param) do local val, fullname = a_with_name(par) if val then return val, alias(fullname) end end return nil end -- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base -- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned. local function a(param) return (a_with_name(param)) end -- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's. local function aurl_with_name(param) local value, fullname = a_with_name(param) if value and value:find(" ") and not value:find("%[") then error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value)) end return value, fullname end -- Identical to a(param) except that it verifies that no space is present. Should be used for URL's. local function aurl(param) return (aurl_with_name(param)) end -- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have -- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string. -- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second -- return value. local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func) local val, fullname = a_with_name(param) local obj = parse_annotated_text(val, fullname) return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname end -- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have -- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string. -- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see -- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g. -- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss. local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func) return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)) end -- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may -- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a -- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two -- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in -- format_multivalued_annotated_text(). local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func) local val, fullname = a_with_name(param) local objs = parse_multivalued_annotated_text(val, fullname) local num_objs = objs and #objs or 0 return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs end -- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may -- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a -- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in -- format_multivalued_annotated_text(). local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func) return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)) end -- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by -- Mary Bloggs" (if there's an author preceding). local author_outputted = false -- When formatting as a citation, the priority is to display a name and a date before the book/chapter title -- this tracks whether or not the author/date has been displayed local date_outputted, formatted_date, formatted_origdate = false local function add_date(no_paren) if not date_outputted then if no_paren then sep = ", " else sep = " " end if formatted_date then if no_paren then add(formatted_date) else add("(" .. formatted_date .. ")") end end if formatted_origdate then add(SPACE_LBRAC .. formatted_origdate .. RBRAC) end sep = ", " date_outputted = true end end local function is_anonymous(val) return val:match("^[Aa]non[yî]mo?u?s?$") or val:match("^[Aa]non%.?$") end -- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split -- among various parameters): -- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is -- the full parameter name holding that value; -- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and -- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter); -- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`, -- and `authorlink_fullname` is the full parameter name holding that value; -- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of -- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such -- parameter); -- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full -- parameter name holding that value; -- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name -- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for -- no such parameter); -- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full -- parameter name holding that value; -- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name -- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for -- no such parameter). -- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first" local function add_author( author, author_fullname, trans_author, trans_author_fullname, authorlink, authorlink_fullname, trans_authorlink, trans_authorlink_fullname, first, first_fullname, trans_first, trans_first_fullname, last, last_fullname, trans_last, trans_last_fullname, last_first ) local function make_author_with_url(txt, txtparam, authorlink, authorlink_param) if authorlink then if authorlink:find("%[%[") then error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink)) end if txt:find("%[%[") then error(("Can't specify links in %s=%s"):format(txtparam, txt)) end return "[[w:" .. authorlink .. "|" .. txt .. "]]" else return txt end end local num_authorobjs if author then local authorobjs = parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname) num_authorobjs = #authorobjs if num_authorobjs == 1 then if is_anonymous(authorobjs[1].text) then authorobjs[1].text = "nivîskarê anonîm" authorobjs[1].link = "nivîskarê anonîm" end if authorlink then authorobjs[1].text = make_author_with_url( authorobjs[1].text, "|" .. author_fullname, authorlink, "|" .. authorlink_fullname ) authorobjs[1].link = make_author_with_url( authorobjs[1].link, "|" .. author_fullname, authorlink, "|" .. authorlink_fullname ) end if authorobjs[1].gloss and trans_authorlink then authorobjs[1].gloss = make_author_with_url( authorobjs[1].gloss, ("<t:...> in |%s"):format(author_fullname), trans_authorlink, "|" .. trans_author_fullname ) end add(j_lazim(format_multivalued_annotated_text(authorobjs))) elseif trans_authorlink then error( ( "Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the " .. "<t:...> inline modifier attached to the individual entities and put the link directly " .. "in the value of the inline modifier" ):format(trans_authorlink_fullname, author_fullname) ) else -- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills -- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it -- all. local formatted_text = format_multivalued_annotated_text(authorobjs) if authorlink then formatted_text = make_author_with_url( formatted_text, "|" .. author_fullname, authorlink, "|" .. authorlink_fullname ) end add(j_lazim(formatted_text)) end else num_authorobjs = 1 -- Author separated into first name + last name. We don't currently support non-Latin-script -- authors separated this way and probably never will. if first then if last_first then author = last .. ", " .. first else author = first .. " " .. last end else author = last end if authorlink then local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname) end local trans_author if trans_last then if trans_first then trans_author = trans_first .. " " .. trans_last else trans_author = trans_last end if trans_authorlink then local trans_authorparam = trans_first and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname) or "|" .. trans_last_fullname trans_author = make_author_with_url( trans_author, trans_authorparam, trans_authorlink, trans_authorlink_fullname ) end end add(j_lazim(author)) if trans_author then add(SPACE_LBRAC) add(trans_author) add(RBRAC) end end author_outputted = true return num_authorobjs end local function add_authorlike( param, prefix_with_preceding_authors, suffix_without_preceding_authors, suffix_if_multiple, anonymous_suffix ) local delimiter = author_outputted and "and" or ", " local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter) if not entities then return end if is_anonymous(entities) then -- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific -- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix. if not anonymous_suffix then local cleaned_suffix = suffix_without_preceding_authors :gsub("&#32;", " ") :gsub("&nbsp;", " ") :gsub("&#160;", " ") :gsub("&#91;", "[") :gsub("&#93;", "]") cleaned_suffix = mw.text.trim(cleaned_suffix) if not anonymous_suffix then anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$") end if not anonymous_suffix then anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$") end if not anonymous_suffix then anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$") end if not anonymous_suffix then anonymous_suffix = suffix_without_preceding_authors end end add_with_sep(j_lazim("anonîm" .. anonymous_suffix)) else entities = j_lazim(entities) if prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then add_with_sep(prefix_with_preceding_authors .. entities) elseif suffix_if_multiple and num_entities > 1 then add_with_sep(entities .. suffix_if_multiple) else add_with_sep(entities .. suffix_without_preceding_authors) end end author_outputted = true end local function add_authorlabel() local default_authorlabel = a("default-authorlabel") if default_authorlabel and yesno(a("authorlabel"), true) then sep = nil add_with_sep(" " .. default_authorlabel) end end local function has_new_title_or_author() return args["2ndauthor"] or args["2ndlast"] or args.chapter2 or args.title2 or args.tlr2 or args.mainauthor2 or args.editor2 or args.editors2 or args.compiler2 or args.compilers2 or args.director2 or args.directors2 end local function has_newversion() return args.newversion or args.location2 or has_new_title_or_author() end -- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or -- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for -- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with -- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"): -- * chapterurl=: URL of the chapter. -- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>). -- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=). -- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added. -- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series). -- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=). -- -- Returns nil if no value specified for the main parameter, otherwise the formatted value. local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix) local chap, chap_fullname = a_with_name(param) local chap_num, chap_num_fullname = a_with_name(param .. "_number") local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain") if chap_num and chap_plain then error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname)) end local chap_series, chap_series_fullname = parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite) local chap_seriesvolume, chap_seriesvolume_fullname = parse_and_format_annotated_text_with_name(param .. "_seriesvolume") if chap_series then chap_series = ", " .. chap_series end if chap_seriesvolume then if not chap_series then error(("Ji bo |%s= ev %s= lazim e"):format(chap_series_fullname, chap_seriesvolume_fullname)) end chap_series = chap_series .. " (" .. chap_seriesvolume .. ")" end local chapterurl local function make_chapter_with_url(chap) if chapterurl then return "[" .. chapterurl .. " " .. chap .. "]" else return chap end end if not chap then if chap_num then error( ("Ji bo |%s= ev |%s= lazim e; nirxa hejmarî li |%s= binivîse"):format( chap_num_fullname, chap_fullname, chap_fullname ) ) end if chap_plain then chapterurl = aurl(param .. "url") return make_chapter_with_url(chap_plain .. (chap_series or "")) end return nil end local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "") chapterurl = aurl(param .. "url") local formatted if numeric_prefix and get_number(cleaned_chap) then -- Arabic chapter number formatted = numeric_prefix .. make_chapter_with_url(chap) elseif numeric_prefix and cleaned_chap:match("^[mdclxviMDCLXVI]+$") and roman_to_arabic(cleaned_chap, true) and (not overrides[param] or not overrides[param].noroman) then -- Roman chapter number formatted = numeric_prefix .. make_chapter_with_url(upper(chap)) else -- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill") if chap:find("^!") then chap = chap:gsub("^!", "") end -- Must be a chapter name local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param)) chapterobj.text = make_chapter_with_url(chapterobj.text) chapterobj.link = make_chapter_with_url(chapterobj.link) formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "") end if chap_num or chap_plain then -- NOTE: Up above we throw an error if both chap_num and chap_plain are specified. formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")" end if chap_series then formatted = formatted .. chap_series end return formatted end -- This handles everything after displaying the author, starting with the chapter and ending with page, column, -- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to -- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a -- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value. local function postauthor(ind, num_authors, format_as_cite) get_full_paramname = make_get_full_paramname(ind) if author_outputted then add_authorlabel() end local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and") coauthors = j_lazim(coauthors) if coauthors then local with_prefix = "" if author_outputted then with_prefix = "li gel " if num_authors == 1 then sep = " " end end add_with_sep(with_prefix .. coauthors) author_outputted = true end add_authorlike("quotee", "quoting ", ", quotee", ", quotees") if format_as_cite and author_outputted and not date_outputted then add_date() sep = ", " end add_authorlike("chapter_tlr", "terciman: ", ", tercime", nil, " terciman") local function add_sg_and_pl_authorlike(noun, verbed) local sgparam = noun local plparam = noun .. "s" local sgval, sgval_fullname = a_with_name(sgparam) local plval, plval_fullname = a_with_name(plparam) if sgval and plval then error(("Nabe ku hem |%s= hem jî |%s= were bikaranîn"):format(sgval_fullname, plval_fullname)) end if sgval or plval then local verbed_by = verbed .. " ji hêla " local comma_sgnoun = ", " .. noun local comma_plnoun = ", " .. noun .. "s" add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun) add_authorlike(plparam, verbed_by, comma_plnoun) end end local formatted_entry = format_chapterlike("entry", nil, "“", "”") local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”") local function add_entry() if formatted_entry then add_with_sep(formatted_entry) if not a("notitle") then add("li ser ") author_outputted = false else author_outputted = true end formatted_entry = nil end end local function add_chapter() add_entry() if formatted_chapter then add_with_sep(formatted_chapter) if not a("notitle") then add("li ser ") author_outputted = false end formatted_chapter = nil end end local function add_actor_role(format_as_cite) local role = parse_and_format_multivalued_annotated_text("role", "and") role = j_lazim(role) local actor_val, actor_fullname = a_with_name("actor") local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname) local actor = format_multivalued_annotated_text(actor_objs, "and") actor = j_lazim(actor) if format_as_cite then if role then if actor then add_with_sep(actor .. " wekî ") end sep = nil add_with_sep(role) elseif actor then add_with_sep(actor .. " (aktor)") end else if role then add_with_sep("xeberdan ji aliyê " .. role .. " ve") if actor then sep = nil add_with_sep(" (" .. actor .. ")") end elseif actor then add_with_sep(actor .. " (aktor)") end end end if format_as_cite then if date_outputted then add_chapter() end local output_len = #output local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor") mainauthor = j_lazim(mainauthor) if mainauthor then add_with_sep(mainauthor) end -- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag author_outputted = false add_authorlike("tlr", "terciman: ", ", tercime", nil, " terciman") author_outputted = false add_sg_and_pl_authorlike("editor", "edîtor") add_sg_and_pl_authorlike("compiler", "berhevkar") add_sg_and_pl_authorlike("director", "dîrektor") add_authorlike("lyricist", nil, " (gotin)", nil, " gotinnivîs") add_authorlike("lyrics-translator", nil, " (tercime)", nil, " tercimenê gotinan") add_authorlike("composer", nil, " (muzîk)", nil, " bestekar") add_actor_role("format_as_cite") -- if the output length has changed, a credit name has been printed -- and we can print the date if output_len ~= #output then author_outputted = true add_date() end add_chapter() else add_chapter() local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor") mainauthor = j_lazim(mainauthor) if mainauthor then add_with_sep(mainauthor) author_outputted = true end add_authorlike("tlr", "terciman: ", ", tercime", nil, " terciman") add_sg_and_pl_authorlike("editor", "edîtor") add_sg_and_pl_authorlike("compiler", "berhevkar") add_sg_and_pl_authorlike("director", "dîrektor") add_authorlike("lyricist", nil, " (gotin)", nil, " gotinnivîs") add_authorlike("lyrics-translator", nil, " (tercime)", nil, " tercimanê gotinan") add_authorlike("composer", nil, " (muzîk)", nil, " bestekar") end local title, title_fullname = a_with_name("title") local need_comma = false if title then local titleobj = parse_annotated_text(title, title_fullname, a("trans-title")) add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite)) local series = parse_and_format_annotated_text("series") if series then add(" (" .. series) local seriesvolume = parse_and_format_annotated_text("seriesvolume") if seriesvolume then add(SEMICOLON_SPACE .. seriesvolume) end add(")") end need_comma = true elseif ind == "" then if not a("notitle") then add(maintenance_line("Jkx sernavê kitêb an kovarê binivîse")) need_comma = true end end local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl") local url, url_fullname = aurl_with_name("url") local urls, urls_fullname = aurl_with_name("urls") if url and urls then error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname)) end local function verify_title_supplied(url_name) -- There are too many cases of this to throw an error at this time. -- if not title then -- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname)) -- end end if archiveurl or url then verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname) sep = nil add("&lrm;<sup>[" .. (archiveurl or url) .. "]</sup>") elseif urls then verify_title_supplied(urls_fullname) sep = nil add("&lrm;<sup>" .. urls .. "</sup>") add("&lrm;<sup>[https://web.archive.org/" .. urls .. " arşîv]</sup>") end -- display (in Language) if language is provided and is not English and not overriden by termlang or worklang if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then local lang = get_first_lang(args[1] or args.lang) if lang then local langcode = lang:getCode() if not (langcode == "und" or langcode == "en") then local langs = format_langs(args[1] or args.lang) if langs then add(" (in " .. langs .. ")") end end end end if need_comma then sep = ", " end local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition") local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain") if edition and edition_plain then error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname)) end if edition then add_with_sep(edition .. " edition") end if edition_plain then add_with_sep(edition_plain) end -- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually -- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized -- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if -- ind == "2"). Only one of the first three can be specified; an error results if more than one are given. -- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly -- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a -- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed -- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page "). -- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is -- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case -- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers -- (including with optional inline modifiers), and such text is handled appropriately. local function format_numeric_param(paramname, singular_desc) local sgval = a_with_name(paramname) local sgobj = parse_annotated_text(sgval, paramname) local plparamname = paramname .. "s" local plval = a_with_name(plparamname) local plobj = parse_annotated_text(plval, plparamname) local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain") local numspec if not sgval and not plval and not plainval then return elseif plainval and (sgval or plval) then error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname)) elseif sgval and plval then -- if both singular and plural, display "page 1 of 1-10" numspec = singular_desc .. " " .. sgval .. " of " .. plval else -- Merge page= and pages= and treat alike because people often mix them up in both directions. if plainval then numspec = plainval else local val = sgobj and sgobj.text or plobj.text if val == "unnumbered" then numspec = "unnumbered " .. singular_desc else local desc if val:find("^!") then val = val:gsub("^!", "") desc = sgval and singular_desc or pluralize(singular_desc) else local check_val = val if check_val:find("%[") then check_val = remove_links(check_val) -- convert URL's of the form [URL DISPLAY] to the displayed value check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1") end -- in case of negative page numbers (do they exist?), don't treat as multiple pages check_val = check_val:gsub("^%-", "") -- replace HTML entity en-dashes and em-dashes with their literal codes check_val = check_val:gsub("&ndash;", "–") check_val = check_val:gsub("&#8211;", "–") check_val = check_val:gsub("&mdash;", "—") check_val = check_val:gsub("&#8212;", "—") -- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b) -- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators). if umatch(check_val, "[–—]") or check_val:find(" and ") or check_val:match("%d+[^ ]* *%- *%d+") or check_val:match("%d+[^ ]* *, +%d+") then desc = pluralize(singular_desc) else desc = singular_desc end end local obj = sgobj or plobj obj.text = val if obj.link:find("^!") then obj.link = obj.link:gsub("^!", "") end val = format_annotated_text(obj) numspec = desc .. " " .. val end end end local url = a(paramname .. "url") if url then return "[" .. url .. " " .. numspec .. "]" else return numspec end end local volume = format_numeric_param("volume", a("volume_prefix") or "volume") if volume then add_with_sep(volume) end local issue = format_numeric_param("issue", a("issue_prefix") or "number") if issue then add_with_sep(issue) end -- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number) local number = format_numeric_param("number", a("number_prefix") or "number") if number then add_with_sep(number) end local annotations = {} local genre = a("genre") if genre then insert(annotations, genre) end local format = a("format") if format then insert(annotations, format) end local medium = a("medium") if medium then insert(annotations, medium) end -- Now handle the display of language annotations like "(in French)" or -- "(quotation in Nauruan; overall work in German)". local quotelang = args[1] or args.lang if not quotelang then if ind == "" then -- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown -- above. insert(annotations, maintenance_line("Please specify the language of the quote using |1=")) else -- do nothing in newversion= portion end elseif ind == "" then local worklang = a("worklang") local termlang = a("termlang") worklang = worklang or quotelang termlang = termlang or quotelang if worklang == quotelang then if worklang == termlang then -- do nothing else insert(annotations, "in " .. format_langs(quotelang)) end else if quotelang ~= termlang then insert(annotations, "quotation in " .. format_langs(quotelang)) end insert(annotations, "overall work in " .. format_langs(worklang)) end else local lang2 = a("lang2") if lang2 then insert(annotations, "in " .. format_langs(lang2)) end end if #annotations > 0 then sep = nil add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")") end local artist = parse_and_format_multivalued_annotated_text("artist", "and") artist = j_lazim(artist) if artist then add_with_sep("performed by " .. artist) end local feat = parse_and_format_multivalued_annotated_text("feat", "and") feat = j_lazim(feat) if feat then sep = " " add_with_sep("ft. " .. feat) end if not format_as_cite then add_actor_role() end local others = parse_and_format_annotated_text("others") others = j_lazim(others) if others then add_with_sep(others) end local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite) if quoted_in then add_with_sep("quoted in " .. quoted_in) insert(tracking_categories, "Quotations using quoted-in parameter") end local location = parse_and_format_multivalued_annotated_text("location", "; ") local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ") if publisher then if location then add_with_sep(location) -- colon sep = "&#58; " -- colon end add_with_sep(publisher) elseif location then add_with_sep(location) end if not date_outputted then add_date("no_paren") end local source = parse_and_format_multivalued_annotated_text("source", "and") if source then add_with_sep("sourced from " .. source) end local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite) local by = parse_and_format_multivalued_annotated_text("by", "and") by = j_lazim(by) local origtype = a("deriv") or "translation" if original or by then add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or "")) end -- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly -- given. local origlang = a("origlang") local origworklang = a("origworklang") local origlangtext, origworklangtext if origlang then origlangtext = "in " .. format_langs(origlang) end if origworklang then origworklangtext = "overall work in " .. format_langs(origworklang) end if origlang or origworklang then if original or by then local orig_annotations = {} if origlangtext then insert(orig_annotations, origlangtext) end if origworklangtext then insert(orig_annotations, origworklangtext) end sep = nil add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")") else add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or "")) if origworklangtext then sep = nil add_with_sep(" (" .. origworklangtext .. ")") end end end if ind ~= "" and has_newversion() then local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true) if formatted_new_date then add_with_sep(formatted_new_date) end end -- Fetch date_published=/year_published=/month_published= and format appropriately. local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true) local platform = parse_and_format_multivalued_annotated_text("platform", "and") if formatted_date_published then add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or "")) elseif platform then add_with_sep("via " .. platform) end -- From here on out, there should always be a preceding item, so we -- can dispense with add_with_sep() and always insert the comma. sep = nil local function small(txt) add(", <small>") add(txt) add("</small>") end -- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params` -- is a string identifying the base param, or a list of such strings to check in turn. If found, the value -- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()), -- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`, -- and running the resulting string through small(), which first adds a comma and then the URL in small font. local function add_identifier(param_or_params, pretext, posttext, process) local val = a(param_or_params) if val then val = (process or mw.uri.encode)(val) small(pretext .. val .. posttext) end end add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]") add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>") add_identifier("isbn", "", "", isbn) add_identifier("issn", "", "", issn) add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]") add_identifier("lccn", "", "", lccn) add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]") add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]") add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]") add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]") add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]") -- add_identifier("urn", "", "", urn) local id = a("id") if id then small(id) end archiveurl, archiveurl_fullname = aurl_with_name("archiveurl") if archiveurl then add(", archived from ") local url, url_fullname = aurl_with_name("url") if not url then -- attempt to infer original URL from archive URL; this works at -- least for Wayback Machine (web.archive.org) URL's url = archiveurl:match("/(https?:.*)$") if not url then error( ("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname) ) end end add("[" .. url .. " the original] on ") local archivedate, archivedate_fullname = a_with_name("archivedate") if archivedate then add(format_date(archivedate)) elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then -- If the archive is from the Wayback Machine, then it already contains the date -- Get the date and format it local wayback_date = string.sub(archiveurl, 29, 29 + 7) wayback_date = string.sub(wayback_date, 1, 4) .. "-" .. string.sub(wayback_date, 5, 6) .. "-" .. string.sub(wayback_date, 7, 8) add(format_date(wayback_date)) else error( ("When |%s= is specified, |%s= must also be included"):format( archiveurl_fullname, archivedate_fullname ) ) end end if a("accessdate") then --Otherwise do not display here, as already used as a fallback for missing date= or year= earlier. if (a("date") or a("nodate") or a("year")) and not a("archivedate") then add(", retrieved " .. format_date(a("accessdate"))) end end local formatted_section = format_chapterlike("section", "section ") if formatted_section then add(", ") add(formatted_section) end -- video game stuff local system = parse_and_format_annotated_text("system") if system then add(", " .. system) end local scene = parse_and_format_annotated_text("scene") if scene then add(", scene: " .. scene) end local level = parse_and_format_annotated_text("level") if level then add(", level/area: " .. level) end local note = parse_and_format_annotated_text("note") if note then add(", " .. note) end local note_plain = parse_and_format_annotated_text("note_plain") if note_plain then add(" " .. note_plain) end -- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text. local function handle_numeric_param(paramname, singular_desc, pretext) local numspec = format_numeric_param(paramname, singular_desc) if numspec then add((pretext or "") .. numspec) end end handle_numeric_param("page", a("page_prefix") or "page", ", ") handle_numeric_param("column", a("column_prefix") or "column", ", ") handle_numeric_param("line", a("line_prefix") or "line", ", ") -- FIXME: Does this make sense? What is other=? local other = parse_and_format_annotated_text("other") if other then add(", " .. other) end end local function add_authors(args, last_first) -- Find maximum indexed author or last name. local maxind = math.max(args.author.maxindex, args.last.maxindex) -- Include max index of ancillary params so we get an error message about their use without the primary params. local ancillary_params = {"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"} for _, ancillary in ipairs(ancillary_params) do maxind = math.max(maxind, args[ancillary].maxindex) end local num_authors = 0 for i = 1, maxind do local ind = i == 1 and "" or i local author, last = args.author[i], args.last[i] if author or last then local this_num_authors = add_author( author, "author" .. ind, args["trans-author"][i], "trans-author" .. ind, args.authorlink[i], "authorlink" .. ind, args["trans-authorlink"][i], "trans-authorlink" .. ind, args.first[i], "first" .. ind, args["trans-first"][i], "trans-first" .. ind, last, "last" .. ind, args["trans-last"][i], "trans-last" .. ind, last_first ) num_authors = num_authors + this_num_authors sep = last_first and "; " or ", " else for _, cant_have in ipairs(ancillary_params) do if args[cant_have][i] then error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind)) end end end end return num_authors end local function add_newversion() -- If there's a "newversion" section, add the new-version text. if has_newversion() then sep = nil --Test for new version of work. add(SEMICOLON_SPACE) if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as". add(args.newversion) elseif not args.edition2 then if has_new_title_or_author() then add("republished as") else add("republished") end end add(" ") return "" else return ", " end end ------------------- Now we start outputting text ---------------------- local need_comma = false -- Set this now so a() works just below. get_full_paramname = make_get_full_paramname("") if args.brackets then add("[") end bold_year = not format_as_cite year_last = format_as_cite formatted_date, need_date = format_date_args( a, get_full_paramname, alias_map, nil, nil, bold_year, "Can we [[:Category:Requests for date|date]] this quote?", year_last ) -- format_date_args bolds the year here (via bold_year = not format_as_cite), so this is the only date variable -- we wrap; origdate=, date_published= and the newversion date are left unwrapped. formatted_date = j_lazim(formatted_date) -- Fetch origdate=/origyear=/origmonth= and format appropriately. formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last) local num_authors, need_date if format_as_cite then num_authors = add_authors(args, "last_first") if author_outputted then sep = " " end -- Display all the text that comes after the author, for the main portion. postauthor("", num_authors, "format_as_cite") author_outputted = false sep = add_newversion() -- Add the newversion author(s). if args["2ndauthor"] or args["2ndlast"] then num_authors = add_author( args["2ndauthor"], "2ndauthor", nil, nil, args["2ndauthorlink"], "2ndauthorlink", nil, nil, args["2ndfirst"], "2ndfirst", nil, nil, args["2ndlast"], "2ndlast", nil, nil, "last_first" ) sep = ", " else for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do if args[cant_have] then error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have)) end end end -- Display all the text that comes after the author, for the "newversion" section. postauthor(2, num_authors, "format_as_cite") else if formatted_date then need_comma = true add(formatted_date) end if formatted_origdate then need_comma = true add(SPACE_LBRAC .. formatted_origdate .. RBRAC) end if need_comma then sep = ", " end date_outputted = true num_authors = add_authors(args) -- Display all the text that comes after the author, for the main portion. postauthor("", num_authors) author_outputted = false sep = add_newversion() -- Add the newversion author(s). if args["2ndauthor"] or args["2ndlast"] then num_authors = add_author( args["2ndauthor"], "2ndauthor", nil, nil, args["2ndauthorlink"], "2ndauthorlink", nil, nil, args["2ndfirst"], "2ndfirst", nil, nil, args["2ndlast"], "2ndlast", nil, nil ) sep = ", " else for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do if args[cant_have] then error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have)) end end end -- Display all the text that comes after the author, for the "newversion" section. postauthor(2, num_authors) end if not args.nocolon then sep = nil add(":") elseif args.usenodot and not args.nodot then add(".") end -- Concatenate output portions to form output text. local output_text = concat(output) -- Remainder of code handles adding categories. We add one or more of the following categories: -- -- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to -- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace -- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same -- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances -- because typically when there's no quotation to format, it's because it's formatted manually underneath the -- citation, or using {{ja-x}}, {{th-x}} or similar.) -- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace, -- Reconstruction:, Appendix: and Citations: pages unless nocat= is given. -- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:, -- Appendix: and Citations: pages. local categories = {} local termlang = get_first_lang(args.termlang or argslang) if args.nocat then if format_as_cite then insert(tracking_categories, "Citations using nocat parameter") else insert(tracking_categories, "Quotations using nocat parameter") end else -- don't create language tracking categories for citations if not format_as_cite then local title if args.pagename then -- for testing, doc pages, etc. title = mw.title.new(args.pagename) if not title then error(("Bad value for `args.pagename`: '%s'"):format(args.pagename)) end else title = mw.title.getCurrentTitle() end -- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists. local ok_to_add_cat if title.nsText ~= "Citations" then ok_to_add_cat = true else local mainspace_title = mw.title.new(title.text) if mainspace_title and mainspace_title.exists then ok_to_add_cat = true end end if ok_to_add_cat then insert(categories, termlang:getFullName() .. " terms with quotations") end end if need_date then local argslangobj = get_first_lang(argslang) insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries") end end return output_text .. ( not lang and "" or (#categories > 0 and format_categories(categories, lang, args.sort) or "") .. ( #tracking_categories > 0 and format_categories( tracking_categories, lang, args.sort, nil, not page_should_be_ignored(mw.title.getCurrentTitle().fullText) ) or "" ) ) end -- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical -- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2" -- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should -- process this spec if type2= is being handled. local type_alias_specs = { av = { {"author", "writer,writers", true}, {"chapter", "episode", true}, {"chapterurl", "episodeurl", true}, {"trans-chapter", "trans-episode", true}, {"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true}, {"chapter_series", "episode_series", true}, {"chapter_seriesvolume", "episode_seriesvolume", true}, {"chapter_number", "episode_number", true}, {"chapter_plain", "episode_plain", true}, {"volume", "season", true}, {"volumes", "seasons", true}, {"volume_plain", "season_plain", true}, {"volumeurl", "seasonurl", true}, {"platform", "network", true}, }, book = { {"author", "3"}, {"editor", "amadekar"}, {"title", ",4"}, {"url", "5"}, {"year", "2"}, {"page", "6"}, {"text", "7"}, {"t", "8"}, }, journal = { {"year", "2"}, {"author", "3"}, {"chapter", "title,sernav,article,meqale,4", true}, {"chapterurl", "titleurl,articleurl,urlya-sernav,urlya-meqaleyê", true}, {"trans-chapter", "trans-title,trans-article,tercimeya-sernav,tercimeya-meqaleyê", true}, {"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators,tercimanê-sernav,tercimanên-sernav,tercimanên-meqaleyê,tercimanê-meqaleyê", true}, {"chapter_series", "title_series,article_series,seriya-sernav,seriya-meqaleyê", true}, {"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume,cildê-seriya-sernav,cildê-seriya-meqaleyê", true}, {"chapter_number", "title_number,article_number,hejmara-sernav,hejmara-meqaleyê", true}, {"chapter_plain", "title_plain,article_plain,sernav-sade,meqale-sade", true}, {"title", "journal,kovar,magazine,newspaper,work,xebat,xezete,5", true}, {"trans-title", "trans-journal,tercimeya-kovar,trans-magazine,trans-newspaper,tercimeya-xezete,trans-work,tercimeya-xebat", true}, {"tlr", "journal_tlr,tercimanê-kovarê,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,tercimanê-xezeteyê,newspaper_translators,work_tlr,tercimanê-xebatê,work_tlrs,work_translator,work_translators", true}, {"url", "6,journalurl,urlya-kovarê,magazineurl,newspaperurl,urlya-xezeteyê,workurl,urlya-xebatê", true}, {"page", "7"}, {"source", "newsagency,ajans,news_agency", true}, {"text", "8"}, {"t", "9"}, }, } -- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of -- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings -- seen. local function process_type_aliases(args, typ, newversion, alias_map) local ind = newversion and "2" or "" local deprecated = ine(args.lang) if not type_alias_specs[typ] then local possible_values = {} for possible, _ in pairs(type_alias_specs) do insert(possible_values, possible) end sort(possible_values) error( ("Unrecognized value '%s' for type%s=; possible values are %s"):format( typ, ind, concat(possible_values, ",") ) ) end for _, alias_spec in ipairs(type_alias_specs[typ]) do local canon, aliases, with_newversion = unpack(alias_spec) if with_newversion or not newversion then canon = canon .. ind aliases = split(aliases, ",", true) local saw_alias = nil for _, alias in ipairs(aliases) do if alias:match("^%d+$") then alias = tonumber(alias) if deprecated then alias = alias - 1 end else alias = alias .. ind end if args[alias] then if saw_alias == nil then saw_alias = alias else error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias)) end end end if saw_alias and (not newversion or type(saw_alias) == "string") then if args[canon] then error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon)) end args[canon] = args[saw_alias] -- Wipe out the original after copying. This is important in case of a param that has general significance -- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and -- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled -- parameter checking. args[saw_alias] = nil alias_map[canon] = saw_alias end end end end -- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores. local function clone_args(direct_args, parent_args) local args = {} -- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is -- specified but is blank, it will still override the parent arg (with nil). for pname, param in pairs(parent_args) do -- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]] track("param/" .. pname) args[pname] = ine(param) end -- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to -- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and -- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias -- processing, saying that title= and its alias album= are both present. local ignores = ine(direct_args.ignore) if ignores then for ignore in gsplit(ignores, "%s*,%s*") do args[ignore] = nil end end local alias_map = {} local other_controls = {} -- noroman local noroman = ine(direct_args.noroman) if noroman then other_controls.overrides = other_controls.overrides or {} for param in gsplit(noroman, "%s*,%s*") do other_controls.overrides[param] = (other_controls.overrides[param] or {}) other_controls.overrides[param].noroman = true end end -- Process internally-specified aliases using type= or type2=. local typ = args.type or direct_args.type or args.cure or direct_args.cure if typ then process_type_aliases(args, typ, false, alias_map) end local typ2 = args.type2 or direct_args.type2 or args.cure2 or direct_args.cure2 if typ2 then process_type_aliases(args, typ2, true, alias_map) end -- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which -- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an -- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for -- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or -- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias -- must be specified before the journal/work -> title alias. -- -- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so -- that when we would display an error message about DEST, we display SOURCE instead. -- -- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up -- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does -- this). -- -- FIXME: Delete this once we've converted all alias processing to internal. local aliases = ine(direct_args.alias) if aliases then -- Allow and discard a trailing semicolon, to make managing multiple aliases easier. aliases = ugsub(aliases, "%s*;$", "") for alias_spec in gsplit(aliases, "%s*;%s*") do local alias_spec_parts = split(alias_spec, "%s*:%s*") if #alias_spec_parts ~= 2 then error(("Alias spec '%s' should have one colon in it"):format(alias_spec)) end local dest, sources = unpack(alias_spec_parts) sources = split(sources, "%s*,%s*") local saw_source = nil for _, source in ipairs(sources) do if source:match("^%d+$") then source = tonumber(source) end if args[source] then if saw_source == nil then saw_source = source else error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source)) end end end if saw_source then if args[dest] then error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest)) end args[dest] = args[saw_source] -- Wipe out the original after copying. This important in case of a param that has general significance -- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and -- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled -- parameter checking. args[saw_source] = nil alias_map[dest] = saw_source end end end -- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen -- in parent_args (they are params we overwrite in the direct args). local error_ifs = ine(direct_args.error_if) if error_ifs then for error_if in gsplit(error_ifs, "%s*,%s*") do if ine(parent_args[error_if]) then error( ("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format( error_if, ine(parent_args[error_if]) ) ) end end end for pname, param in pairs(direct_args) do -- ignore control params if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then args[pname] = ine(param) end end return args, alias_map, other_controls end local function get_args(frame_args, parent_args, require_lang) -- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is -- wasteful of memory. local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args) local deprecated = ine(parent_args.lang) local alias_of_t = {alias_of = "t"} local boolean = {type = "boolean"} local language_sublist = {type = "language", sublist = true} local list_allow_holes = {list = true, allow_holes = true} local script = {type = "script"} local function alias_list_allow_holes(canon) return {alias_of = canon, list = true, allow_holes = true} end -- First, the "single" params that don't have FOO2 or FOOn versions. local params = { [deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"}, ["lang2"] = language_sublist, ["ziman2"] = {alias_of = "lang2", type = "language", sublist = true}, ["newversion"] = true, ["versiyona-nû"] = {alias_of = "newversion"}, ["author"] = list_allow_holes, ["nivîskar"] = alias_list_allow_holes("author"), ["2ndauthor"] = true, ["2nivîskar"] = {alias_of = "2ndauthor"}, ["trans-author"] = list_allow_holes, ["tercimeya-nivîskar"] = alias_list_allow_holes("trans-author"), ["authorlink"] = list_allow_holes, ["lînka-nivîskar"] = alias_list_allow_holes("authorlink"), ["2ndauthorlink"] = true, ["2lînka-nivîskar"] = {alias_of = "2ndauthorlink"}, ["trans-authorlink"] = list_allow_holes, ["lînka-tercimeya-nivîskar"] = alias_list_allow_holes("trans-authorlink"), ["first"] = list_allow_holes, ["pêşnav"] = alias_list_allow_holes("first"), ["2ndfirst"] = true, ["2pêşnav"] = {alias_of = "2ndfirst"}, ["trans-first"] = list_allow_holes, ["tercimeya-pêşnav"] = alias_list_allow_holes("trans-first"), ["last"] = list_allow_holes, ["paşnav"] = alias_list_allow_holes("last"), ["2ndlast"] = true, ["2paşnav"] = {alias_of = "2ndlast"}, ["trans-last"] = list_allow_holes, ["tercimeya-paşnav"] = alias_list_allow_holes("trans-last"), ["nocat"] = boolean, ["nekat"] = {alias_of = "nocat"}, ["nocolon"] = boolean, -- quote params text = true, passage = {alias_of = "text"}, ["jêgirtin"] = {alias_of = "text"}, ["jg"] = {alias_of = "text"}, tr = true, transliteration = {alias_of = "tr"}, ts = true, transcription = {alias_of = "ts"}, norm = true, normalization = {alias_of = "norm"}, sc = script, normsc = script, sort = true, subst = true, footer = true, lit = true, ["hrf"] = {alias_of = "lit"}, t = true, translation = alias_of_t, ["tercime"] = alias_of_t, ["werger"] = alias_of_t, ["w"] = alias_of_t, gloss = alias_of_t, lb = true, ["e"] = {alias_of = "lb"}, brackets = boolean, -- original quote params origtext = true, ["metna-orîjînal"] = {alias_of = "origtext"}, origtr = true, ["tr-orîjînal"] = {alias_of = "origtr"}, origts = true, ["ts-orîjînal"] = {alias_of = "origts"}, orignorm = true, ["norm-orîjînal"] = {alias_of = "orignorm"}, origsc = script, ["sc-orîjînal"] = {alias_of = "origsc"}, orignormsc = script, ["normsc-orîjînal"] = {alias_of = "orignormsc"}, origsubst = true, ["subst-orîjînal"] = {alias_of = "origsubst"}, origlb = true, ["e-orîjînal"] = {alias_of = "origlb"}, usenodot = boolean, nodot = boolean, inline = boolean, asquote = boolean, ascite = boolean, } -- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to -- allow PARAMN variants for any N. local function add_with_2(param, value) params[param] = value params[param .. "2"] = value end local function alias_with_2(alias, canon) params[alias] = {alias_of = canon} params[alias .. "2"] = {alias_of = canon .. "2"} end for _, param12 in ipairs{ "worklang", "termlang", "origlang", "origworklang" } do add_with_2(param12, language_sublist) end -- Then the newversion params (which have FOO2 versions). for _, param12 in ipairs{ -- author-like params; author params themselves are either list params (author=, last=, etc.) or single params -- (2ndauthor=, 2ndlast=, etc.) "coauthors", "quotee", "tlr", "editor", "editors", "mainauthor", "compiler", "compilers", "director", "directors", "lyricist", "lyrics-translator", "composer", "role", "actor", "artist", "feat", -- author control params "default-authorlabel", "authorlabel", -- title "title", "trans-title", "series", "seriesvolume", "notitle", -- entry "entry", "entryurl", "trans-entry", -- chapter "chapter", "chapterurl", "chapter_number", "chapter_plain", "chapter_series", "chapter_seriesvolume", "trans-chapter", "chapter_tlr", -- section "section", "sectionurl", "section_number", "section_plain", "section_series", "section_seriesvolume", "trans-section", -- other video-game params "system", "scene", "level", -- URL "url", "urls", "archiveurl", -- edition "edition", "edition_plain", -- ID params "bibcode", "doi", "isbn", "issn", "jstor", "lccn", "oclc", "ol", "pmid", "pmcid", "ssrn", "urn", "id", -- misc date params; most date params handled below "archivedate", "accessdate", "nodate", -- numeric params handled below -- other params "type", "genre", "format", "medium", "others", "quoted_in", "location", "publisher", "original", "by", "deriv", "note", "note_plain", "other", "source", "platform", } do add_with_2(param12, true) end -- Then the aliases of newversion params (which have FOO2 versions). for _, param12_aliased in ipairs{ {"role", "roles"}, {"role", "speaker"}, {"tlr", "tlrs"}, {"tlr", "translator"}, {"tlr", "translators"}, {"chapter_tlr", "chapter_tlrs"}, {"chapter_tlr", "chapter_translator"}, {"chapter_tlr", "chapter_translators"}, {"doi", "DOI"}, {"isbn", "ISBN"}, {"issn", "ISSN"}, {"jstor", "JSTOR"}, {"lccn", "LCCN"}, {"oclc", "OCLC"}, {"ol", "OL"}, {"pmid", "PMID"}, {"pmcid", "PMCID"}, {"ssrn", "SSRN"}, {"urn", "URN"}, } do local canon, alias = unpack(param12_aliased) alias_with_2(alias, canon) end -- tercimeyên Then the aliases of newversion params (which have FOO2 versions). for _, param12_aliased in ipairs{ {"role", "rol"}, {"role", "axivêr"}, {"tlr", "terciman"}, {"tlr", "wergêr"}, {"chapter_tlr", "tercimanên-beşê"}, {"chapter_tlr", "tercimanê-beşê"}, {"chapter_tlr", "tercimana-beşê"}, {"chapter_tlr", "wergêrên-beşê"}, {"chapter_tlr", "wergêrê-beşê"}, {"chapter_tlr", "wergêra-beşê"}, -- tercimeyên Then the newversion params (which have FOO2 versions). -- author-like params; author params themselves are either list params (author=, last=, etc.) or single params -- (2ndauthor=, 2ndlast=, etc.) {"coauthors", "hevnivîskar"}, {"quotee", "jê-tê-girtin"}, {"editor", "edîtor"}, {"editors", "edîtor"}, {"editor", "amadekar"}, {"mainauthor", "nivîskarê-sereke"}, {"compiler", "berhevkar"}, {"compilers", "berhevkar"}, {"director", "derhêner"}, {"directors", "derhêner"}, {"lyricist", "gotinnivîs"}, {"lyrics-translator", "tercimanê-gotinan"}, {"lyrics-translator", "tercimana-gotinan"}, {"lyrics-translator", "wergêrê-gotinan"}, {"lyrics-translator", "wergêra-gotinan"}, {"composer", "bestekar"}, {"actor", "aktor"}, {"actor", "lîstikvan"}, {"artist", "hunermend"}, {"artist", "artîst"}, {"feat", "mêvan"}, --tercimeyên author control params {"default-authorlabel", "standard-etîketa-nivîskar"}, {"authorlabel", "etîketa-nivîskar"}, --tercimeyên title {"title", "sernav"}, {"trans-title", "tercimeya-sernav"}, {"trans-title", "wergera-sernav"}, {"series", "serî"}, {"series", "rêze"}, {"seriesvolume", "cildê-seriyê"}, {"seriesvolume", "cildê-rêzeyê"}, {"notitle", "bêsernav"}, --tercimeyên entry {"entry", "made"}, {"entryurl", "urlya-madeyê"}, {"trans-entry", "tercimeya-madeyê"}, {"trans-entry", "wergera-madeyê"}, --tercimeyên chapter {"chapter", "beş"}, {"chapterurl", "urlya-beşê"}, {"chapter_number", "hejmara-beşê"}, {"chapter_plain", "beş-sade"}, {"chapter_series", "seriya-beşê"}, {"chapter_series", "rêzeya-beşê"}, {"chapter_seriesvolume", "cildê-seriya-beşê"}, {"chapter_seriesvolume", "cildê-rêzeya-beşê"}, {"trans-chapter", "tercimeya-beşê"}, {"trans-chapter", "wergera-beşê"}, --tercimeyên section {"section", "qisim"}, {"section", "qism"}, {"sectionurl", "urlya-qismê"}, {"section_number", "hejmara-qismê"}, {"section_plain", "qisim-sade"}, {"section_plain", "qism-sade"}, {"section_series", "rêzeya-qismê"}, {"section_seriesvolume", "cildê-seriya-qismê"}, {"section_seriesvolume", "cildê-rêzeya-qismê"}, {"trans-section", "tercimeya-qismê"}, --tercimeyên other video-game params {"system", "sîstem"}, {"system", "pergal"}, {"scene", "sehne"}, --{"level", "level"}, --tercimeyên URL {"archiveurl", "urlya-arşîvê"}, -- tercimeyên edition {"edition", "çap"}, {"edition_plain", "çap-sade"}, --tercimeyên misc date params; most date params handled below {"archivedate", "roja-arşîvê"}, {"archivedate", "tarîxa-arşîvê"}, {"accessdate", "tarîxa-gihiştinê"}, {"accessdate", "roja-gihiştinê"}, {"nodate", "bêtarîx"}, --tercimeyên other params {"type", "cure"}, {"genre", "janr"}, {"medium", "medya"}, {"others", "kesên-din"}, {"others", "yên-din"}, {"quoted_in", "cihê-jêgirtinê"}, {"location", "cih"}, {"location", "cîh"}, {"publisher", "weşanxane"}, {"publisher", "weşanger"}, {"original", "orîjînal"}, {"original", "orjînal"}, {"by", "ji-aliyê"}, {"by", "ji-hêla"}, --{"deriv", ""}, {"note", "not"}, {"note_plain", "not-sade"}, {"other", "yê-din"}, {"source", "çavkanî"}, --tercimeyên {"worklang", "zimanê-eserê"}, {"termlang", "zimanê-termê"}, {"origlang", "zimanê-orîjînal"}, {"origworklang", "zimanê-esera-orîjînal"}, } do local canon, alias = unpack(param12_aliased) alias_with_2(alias, canon) end -- Then the date params. for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do local pref, suf = unpack(datelike) for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do add_with_2(pref .. arg .. suf, true) end end -- Kurmancî aliases for the date params for _, date_aliased in ipairs{ {"date", "tarîx"}, {"date", "dîrok"}, {"year", "sal"}, {"month", "meh"}, {"start_date", "tarîxa-destpêkê"}, {"start_year", "sala-destpêkê"}, {"start_month", "meha-destpêkê"}, {"origdate", "tarîxa-orîjînal"}, {"origyear", "sala-orîjînal"}, {"origyear", "sala orîjînal"}, {"origmonth", "meha-orîjînal"}, {"origstart_date", "tarîxa-destpêkê-orîjînal"}, {"origstart_year", "sala-destpêkê-orîjînal"}, {"origstart_month", "meha-destpêkê-orîjînal"}, {"date_published", "tarîxa-weşandinê"}, {"year_published", "sala-weşandinê"}, {"year_published", "sala weşandinê"}, {"month_published", "meha-weşandinê"}, {"start_date_published", "tarîxa-destpêkê-weşandin"}, {"start_year_published", "sala_destpêkê-weşandin"}, {"start_month_published", "meha-destpêkê-weşandin"} } do local canon, alias = unpack(date_aliased) alias_with_2(alias, canon) end local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"} -- Then the numeric params. for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do for _, suf in ipairs(numeric_param_suffixes) do add_with_2(numeric .. suf, true) end end -- canon word -> Kurdish forms -- aliases : plain synonyms (maps straight to canon, e.g. "volume") -- sade : ezafe form used before "-sade" (canon .. "_plain") -- urlya : ezafe form used after "urlya-" (canon .. "url") local ku_numeric = { volume = {aliases = {"cild", "cîld"}, sade = "cild", urlya = "cild"}, issue = {aliases = {"hj", "hejmar"}, sade = "hejmar", urlya = "hejmarê"}, number = {aliases = {"hj", "hejmar"}, sade = "hejmar", urlya = "hejmarê"}, line = {aliases = {"rêz"}, sade = "rêz", urlya = "rêzê"}, page = {aliases = {"rûpel", "r"}, sade = "rûpel", urlya = "rûpelê"}, column = {aliases = {"stûn"}, sade = "stûn", urlya = "stûnê"}, } for canon, t in pairs(ku_numeric) do for _, alias in ipairs(t.aliases) do alias_with_2(alias, canon) end alias_with_2(t.sade .. "-sade", canon .. "_plain") alias_with_2("urlya-" .. t.urlya, canon .. "url") alias_with_2("pêşgira-" .. t.urlya, canon .. "_prefix") end -- And the aliases of numeric params. for _, numeric_aliased in ipairs{{"volume", "vol"}} do local canon, alias = unpack(numeric_aliased) for _, suf in ipairs(numeric_param_suffixes) do alias_with_2(alias .. suf, canon .. suf) end end return process_params(cloned_args, params), alias_map, other_controls end local function get_origtext_params(args) local origtext, origtextlang, origsc, orignormsc if args.origtext then -- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or -- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology -- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been -- eliminated.) origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$") if not origtextlang then -- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL. origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$") end if not origtextlang then error("origtext= should begin with a language code prefix") end origtextlang = get_lang(origtextlang, nil, "allow etym") or error("origtext= should begin with a language code prefix") origsc = args.origsc orignormsc = args.orignormsc else for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do if args[noparam] then error(("Cannot specify %s= without origtext="):format(noparam)) end end end return origtext, origtextlang, origsc, orignormsc end local function get_quote(args, is_cite, source) local text = args.text local gloss = args.t local tr = args.tr local ts = args.ts local norm = args.norm local sc = args.sc local normsc = args.normsc -- Fetch original-text parameters. local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args) -- If any quote-related args are present, display the actual quote; otherwise, display nothing. if text or gloss or tr or ts or norm or args.origtext then -- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages -- in any case in source() if the language is omitted. local lang = get_first_lang(args[1] or args.lang) local termlang = args.termlang and get_first_lang(args.termlang) or lang local usex_data = { lang = lang, termlang = termlang, usex = text, sc = sc, translation = gloss, normalization = norm, normsc = normsc, transliteration = tr, transcription = ts, brackets = args.brackets, subst = args.subst, lit = args.lit, footer = args.footer, qq = parse_and_format_labels(args.lb, lang), quote = "quote-meta", orig = origtext, origlang = origtextlang, origsc = origsc, orignorm = args.orignorm, orignormsc = orignormsc, origtr = args.origtr, origts = args.origts, origsubst = args.origsubst, origqq = parse_and_format_labels(args.origlb, lang), noreq = args.noreq, nocat = is_cite or args.nocat, source = source } if args.inline then -- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text usex_data.footer = nil usex_data.inline = 1 text = format_usex(usex_data) if text then text = " “" .. text .. "”" else text = "" end if args.footer then text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>" end else text = format_usex(usex_data) end elseif args.footer then text = "<dl><dd>" .. args.footer .. "</dd></dl>" end return text end -- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary -- interface for {{quote-*}} templates. function export.quote_t(frame) local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang") if args.ascite then return export.cite_t(frame) end return show_quote(args, alias_map, other_controls) end function show_quote(args, alias_map, other_controls) local parts = {} insert(parts, get_quote(args, false, export.source(args, alias_map, nil, other_controls))) local retval = concat(parts) return deprecated and frame:expandTemplate({ title = "check deprecated lang param usage", args = {retval, lang = args.lang}, }) or retval end -- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary -- interface for {{cite-*}} templates. function export.cite_t(frame) local parent_args = {} for k, v in pairs(frame:getParent().args) do parent_args[k] = v end -- use "und" as lang if none provided if parent_args[1] == nil then parent_args[1] = "und" end local args, alias_map, other_controls = get_args(frame.args, parent_args) if args.asquote then return show_quote(args, alias_map, other_controls) end return show_cite(args, alias_map, other_controls) end function show_cite(args, alias_map, other_controls) local parts = {} -- don't nag for translations if args.text and not args.t then args.noreq = 1 end local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0 if len_visible == 0 then if not args.t or args.t == "-" then args.nocolon = true end elseif args.inline == nil then args.inline = not ( args.block_text or len_visible > 300 or (args.t and not args.t == "-" and len_visible < 80) or string.match(args.text, "<br>") ) end insert(parts, '<span class="citation-whole"><span class="cited-source">') insert(parts, export.source(args, alias_map, "format_as_cite", other_controls)) insert(parts, "</span>") insert(parts, get_quote(args, "is_cite")) insert(parts, "</span>") local retval = concat(parts) return deprecated and frame:expandTemplate({ title = "check deprecated lang param usage", args = {retval, lang = args.lang}, }) or retval end -- External interface, meant to be called from a template. function export.call_quote_template(frame) return export.call_template(frame) end -- External interface, for calling a template with overloaded parameters. function export.overload(frame) local iargs, overloaded_args = process_params(frame.args, { [1] = {required = true} }, true) local parent_args = frame:getParent().args for k, v in pairs(parent_args) do overloaded_args[k] = v end return frame:expandTemplate{title = iargs[1], args = overloaded_args} end -- External interface, meant to be called from a template. function export.call_template(frame) local parameter_sublist = {type = "parameter", sublist = true} local iargs, other_direct_args = process_params(frame.args, { ["template"] = true, ["textparam"] = parameter_sublist, ["pageparam"] = parameter_sublist, ["propagateparams"] = parameter_sublist, ["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently. }, true) local function fetch_param(source, params) for _, param in ipairs(params) do if source[param] then return source[param] end end return nil end local params = { ["text"] = true, ["passage"] = true, ["footer"] = true, -- always propagates, but gets special formatting } -- paramaters that can be used during template declaration or included in template calls -- if the template is called with param=- it will set any declared value to nil local always_propagate = { ["brackets"] = true, ["usenodot"] = true, ["nodot"] = true, ["ascite"] = true, ["asquote"] = true, } for k,v in pairs(always_propagate) do params[k] = v end local textparam = iargs.textparam or {} for _, param in ipairs(textparam) do params[param] = true end local pageparam = iargs.pageparam or {} local pageparam1 = pageparam[1] if pageparam1 ~= nil then params["page"], params["pages"] = true, true for _, param in ipairs(pageparam) do params[param] = true end end local allowparams, allow_all, list = iargs.allowparams, false if allowparams ~= nil then for _, allow in ipairs(allowparams) do local param = allow:match("^(.*):list$") if param then if list == nil then list = {list = true} end params[scribunto_parameter_key(param)] = list elseif allow == "*" then track("no parameter checking") allow_all = true else params[scribunto_parameter_key(allow)] = true end end else local parent = frame:getParent() local template_name = parent:getTitle() local template_title = new_title(template_name) local content = template_title:getContent() local allowed_params, seen = {}, {} -- Detect all params used by the parent template. param:get_name() takes the -- parent frame arg table as an argument so that preprocessing will take -- them into account, since it will matter if the name contains another -- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change -- depending on the value for bar=). `seen` memoizes results based on the -- raw parameter text (which is stored as a string in the parameter object), -- which avoids unnecessary param:get_name() calls, which are non-trivial. for param in find_parameters(content) do local raw = param.raw if not seen[raw] then allow = param:get_name(template_args) params[scribunto_parameter_key(allow)] = true seen[raw] = true end end end local propagateparams = iargs.propagateparams or {} for _, param in ipairs(propagateparams) do params[param] = true end local parent_args = frame:getParent().args local args = process_params(parent_args, params, allow_all) parent_args = shallow_copy(parent_args) if textparam[1] ~= "-" then other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam) end if not (pageparam1 == nil or pageparam1 == "-") then other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil other_direct_args.pages = args.pages or other_direct_args.pages or nil if other_direct_args.page == "-" then other_direct_args.page = nil end if other_direct_args.pages == "-" then other_direct_args.pages = nil end end if args.footer then other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}} end for param, _ in pairs(always_propagate) do if args[param] == "-" then other_direct_args[param] = nil else other_direct_args[param] = args[param] or other_direct_args[param] or nil end end -- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to -- the actual {{quote-*}} code. if other_direct_args.authorlink == "-" then other_direct_args.authorlink = nil end for _, param in ipairs(propagateparams) do if args[param] then other_direct_args[param] = args[param] end end return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args} end local paramdoc_param_replacements = { passage = { param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}", param_no_synonym = "{{para|text}} or {{para|passage}}", text = [=[ * <<params>> – the passage to be quoted.]=], }, page = { param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}", param_no_synonym = "{{para|page}} or {{para|pages}}", text = [=[ * <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following: ** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}. ** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears). : This parameter must be specified to have the template link to the online version of the work.]=], }, page_with_roman_preface = { param_with_synonym = {"inherit", "page"}, param_no_synonym = {"inherit", "page"}, text = [=[ * <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following: ** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}. ** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears). : This parameter must be specified to have the template link to the online version of the work.]=], }, chapter = { param_with_synonym = "<<synonym>> or {{para|chapter}}", param_no_synonym = "{{para|chapter}}", text = [=[ * <<params>> – the name of the chapter quoted from.]=], }, roman_chapter = { param_with_synonym = {"inherit", "chapter"}, param_no_synonym = {"inherit", "chapter"}, text = [=[ * <<params>> – the chapter number quoted from in uppercase Roman numerals.]=], }, arabic_chapter = { param_with_synonym = {"inherit", "chapter"}, param_no_synonym = {"inherit", "chapter"}, text = [=[ * <<params>> – the chapter number quoted from in Arabic numerals.]=], }, trailing_params = { text = [=[ * {{para|footer}} – a comment on the passage quoted. * {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=], }, } function export.paramdoc(frame) local parargs = frame:getParent().args local args = process_params(parargs, { [1] = true, }) local text = args[1] local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym) local function sub_param(synonym) local subbed_paramtext if synonym then subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}") else subbed_paramtext = paramtext_no_synonym end return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext)))) end text = text:gsub("<<" .. pattern_escape(param) .. ">>", function() return sub_param() end) text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param) end local function fetch_text(param_to_replace, key) local spec = paramdoc_param_replacements[param_to_replace] local val = spec[key] if type(val) == "string" then return val end if type(val) == "table" and val[1] == "inherit" then return fetch_text(val[2], key) end error( "Internal error: Unrecognized value for param '" .. param_to_replace .. "', key '" .. key .. "': " .. mw.dumpObject(val) ) end for param_to_replace, spec in pairs(paramdoc_param_replacements) do if not spec.param_no_synonym then -- Text to substitute directly. text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function() return frame:preprocess(fetch_text(param_to_replace, "text")) end) else do_param_with_optional_synonym( param_to_replace, fetch_text(param_to_replace, "text"), fetch_text(param_to_replace, "param_with_synonym"), fetch_text(param_to_replace, "param_no_synonym") ) end end -- Remove final newline so template code can add a newline after invocation text = text:gsub("\n$", "") return text end return export 90ntgrk4oeqnmvrcp71mwuh5x520a5u 6291572 6291566 2026-07-26T15:19:00Z Wikihez 7344 6291572 Scribunto text/plain --[=[ This module contains functions to implement quote-* templates. Author: Benwing2; conversion into Lua of {{quote-meta/source}} template, written by Sgconlaw with some help from Erutuon and Benwing2. The main interface is quote_t(). Note that the source display is handled by source(), which reads both the arguments passed to it *and* the arguments passed to the parent template, with the former overriding the latter. ]=] local export = {} --[=[ FIXME: 1. Dates like `1 march 2025` with the day of the month being 1 and the month lowercased are treated as month-year-only. The relevant code checks for a capitalized word; it should probably instead check for the actual month names or abbrevs. ]=] -- Named constants for all modules used, to make it easier to swap out sandbox versions. local check_isxn_module = "Module:check isxn" local debug_track_module = "Module:debug/track" local en_utilities_module = "Module:en-utilities" local italics_module = "Module:italics" local labels_module = "Module:labels" local languages_module = "Module:languages" local languages_error_module = "Module:languages/error" local links_module = "Module:links" local number_utilities_module = "Module:number utilities" local parameters_module = "Module:parameters" local parse_utilities_module = "Module:parse utilities" local qualifier_module = "Module:qualifier" local roman_numerals_module = "Module:roman numerals" local scribunto_module = "Module:Scribunto" local script_utilities_module = "Module:script utilities" local scripts_module = "Module:scripts" local string_pattern_escape_module = "Module:string/patternEscape" local string_replacement_escape_module = "Module:string/replacementEscape" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local template_parser_module = "Module:template parser" local usex_module = "Module:usex/ceribandin" local usex_templates_module = "Module:usex/templates" local utilities_module = "Module:utilities" local yesno_module = "Module:yesno" local concat = table.concat local insert = table.insert local new_title = mw.title.new local remove = table.remove local require = require local sort = table.sort local u = mw.ustring.char local ugsub = mw.ustring.gsub local umatch = mw.ustring.match local unpack = unpack or table.unpack -- Lua 5.2 compatibility -- Use HTML entities here to avoid parsing issues (esp. with brackets) local SEMICOLON_SPACE = "&#59; " local SPACE_LBRAC = " &#91;" local RBRAC = "&#93;" local TEMP_LT = u(0xFFF1) local TEMP_GT = u(0xFFF2) local TEMP_LBRAC = u(0xFFF3) local TEMP_RBRAC = u(0xFFF4) local TEMP_SEMICOLON = u(0xFFF5) local function apply_subst(...) apply_subst = require(usex_module).apply_subst return apply_subst(...) end local function check_isbn(...) check_isbn = require(check_isxn_module).check_isbn return check_isbn(...) end local function check_issn(...) check_issn = require(check_isxn_module).check_issn return check_issn(...) end local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function embedded_language_links(...) embedded_language_links = require(links_module).embedded_language_links return embedded_language_links(...) end local function escape_wikicode(...) escape_wikicode = require(parse_utilities_module).escape_wikicode return escape_wikicode(...) end local function find_best_script_without_lang(...) find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang return find_best_script_without_lang(...) end local function find_parameters(...) find_parameters = require(template_parser_module).find_parameters return find_parameters(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function format_processed_labels(...) format_processed_labels = require(labels_module).format_processed_labels return format_processed_labels(...) end local function format_qualifier(...) format_qualifier = require(qualifier_module).format_qualifier return format_qualifier(...) end local function format_usex(...) format_usex = require(usex_module).format_usex return format_usex(...) end local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end local function get_number(...) get_number = require(number_utilities_module).get_number return get_number(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function gsplit(...) gsplit = require(string_utilities_module).gsplit return gsplit(...) end local function page_should_be_ignored(...) page_should_be_ignored = require(usex_templates_module).page_should_be_ignored return page_should_be_ignored(...) end local function parse_inline_modifiers(...) parse_inline_modifiers = require(parse_utilities_module).parse_inline_modifiers return parse_inline_modifiers(...) end local function parse_inline_modifiers_from_segments(...) parse_inline_modifiers_from_segments = require(parse_utilities_module).parse_inline_modifiers_from_segments return parse_inline_modifiers_from_segments(...) end local function parse_multi_delimiter_balanced_segment_run(...) parse_multi_delimiter_balanced_segment_run = require(parse_utilities_module).parse_multi_delimiter_balanced_segment_run return parse_multi_delimiter_balanced_segment_run(...) end local function parse_term_with_lang(...) parse_term_with_lang = require(parse_utilities_module).parse_term_with_lang return parse_term_with_lang(...) end local function pattern_escape(...) pattern_escape = require(string_pattern_escape_module) return pattern_escape(...) end local function pluralize(...) pluralize = require(en_utilities_module).pluralize return pluralize(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) end local function remove_links(...) remove_links = require(links_module).remove_links return remove_links(...) end local function roman_to_arabic(...) roman_to_arabic = require(roman_numerals_module).roman_to_arabic return roman_to_arabic(...) end local function replacement_escape(...) replacement_escape = require(string_replacement_escape_module) return replacement_escape(...) end local function scribunto_parameter_key(...) scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key return scribunto_parameter_key(...) end local function serial_comma_join(...) serial_comma_join = require(table_module).serialCommaJoin return serial_comma_join(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function split_alternating_runs(...) split_alternating_runs = require(parse_utilities_module).split_alternating_runs return split_alternating_runs(...) end local function split_and_process_raw_labels(...) split_and_process_raw_labels = require(labels_module).split_and_process_raw_labels return split_and_process_raw_labels(...) end local function split_on_comma(...) split_on_comma = require(parse_utilities_module).split_on_comma return split_on_comma(...) end local function tag_text(...) tag_text = require(script_utilities_module).tag_text return tag_text(...) end local function tag_transcription(...) tag_transcription = require(script_utilities_module).tag_transcription return tag_transcription(...) end local function tag_translit(...) tag_translit = require(script_utilities_module).tag_translit return tag_translit(...) end local function ulen(...) ulen = require(string_utilities_module).len return ulen(...) end local function unitalicize_brackets(...) unitalicize_brackets = require(italics_module).unitalicize_brackets return unitalicize_brackets(...) end local function upper(...) upper = require(string_utilities_module).upper return upper(...) end local function usub(...) usub = require(string_utilities_module).sub return usub(...) end local function yesno(...) yesno = require(yesno_module) return yesno(...) end local function track(page) debug_track("quote/" .. page) end local function maintenance_line(text) return '<span class="maintenance-line">(' .. text .. ")</span>" end local function isbn(text) return "[[Special:BookSources/" .. text .. "|→ISBN]]" .. check_isbn( text, '&nbsp;<span class="error" style="font-size:88%">Invalid&nbsp;ISBN</span>[[Category:Pages with ISBN errors]]' ) end local function issn(text) return "[https://www.worldcat.org/issn/" .. text .. " →ISSN]" .. check_issn( text, '&nbsp;<span class="error" style="font-size:88%">Invalid&nbsp;ISSN</span>[[Category:Pages with ISSN errors]]' ) end local function lccn(text) text = text:gsub(" ", "") if text:find("-") then -- old-style LCCN; reformat per request by [[User:The Editor's Apprentice]] local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$") if prefix then if ulen(part2) < 6 then part2 = ("0"):rep(6 - ulen(part2)) .. part2 end text = prefix .. part1 .. part2 end end return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]" end local function format_date(text) return mw.getCurrentFrame():callParserFunction("#formatdate", text, "dmy") end -- Parse a raw lb= param (or nil) to individual label info objects and then concatenate them appropriately into a -- qualifier input, respecting flags like `omit_preComma` and `omit_postSpace` in the label specs. local function parse_and_format_labels(raw_lb, lang) if not raw_lb then return nil end local labels = split_and_process_raw_labels{labels = raw_lb, lang = lang, nocat = true} labels = format_processed_labels{labels = labels, lang = lang, no_ib_content = true} if labels ~= "" then -- not sure labels can be an empty string but it seems possible in some circumstances return {labels} end end -- Convert a comma-separated list of language codes to a comma-separated list of language names. `fullname` is the -- name of the parameter from which the list of language codes was fetched. local function format_langs(langs) local names = {} for i, lang in ipairs(langs) do names[i] = lang:getCanonicalName() end if #names == 1 then return names[1] end return serial_comma_join(names) end local function get_first_lang(langs) return langs[1] or get_lang("und") end --[=[ Normally we parse off inline modifiers and language code prefixes in various places, e.g. he:מרים<tr:Miryem>. But we exclude HTML entries with <span ...>, <i ...>, <br/> or similar in it, caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Basically, all tags of the sort we parse here should consist of a less-than sign, plus letters, plus a colon, e.g. <tr:...>, so if we see a tag on the outer level that isn't in this format, we don't try to parse it. The restriction to the outer level is to allow generated HTML inside of e.g. qualifier modifiers, such as foo<q:similar to {{m|fr|bar}}> (if we end up supporting such modifiers). Also exclude things that look like URL's from being parsed as having language code prefixes. ]=] local function val_should_not_be_parsed_for_annotations(val) return val:find("^[^<]*<%l*[^%l:]") or val:find("^%l+://") end local param_mods = { t = { -- <t:...> and <gloss:...> are aliases. item_dest = "gloss", }, gloss = {}, -- Kurmanji aliases of <t:...>/<gloss:...>. w = { item_dest = "gloss", }, werger = { item_dest = "gloss", }, tercime = { item_dest = "gloss", }, alt = {}, -- Kurmanji alias of <alt:...>. cuda = { item_dest = "alt", }, tr = {}, ts = {}, subst = {}, sc = {type = "script"}, f = { convert = function(arg, parse_err) local prefix, val = arg:match("^(.-):([^ ].*)$") if not prefix then prefix = "" val = arg end local tags, sc_code, sc = prefix:match("^(.*)/(.-)$") if sc_code then sc = get_script(sc_code) or require(languages_error_module)(sc_code, parse_err, "script code", nil, "not real lang") else tags = prefix end local quals if tags ~= "" then quals = split_on_comma(tags) for i, qual in ipairs(quals) do local obj = get_lang(qual, nil, "allow etym") or get_script(qual) quals[i] = obj or qual end end return { quals = quals, sc = sc, val = val, } end, store = "insert", }, q = {}, qq = {}, } local function generate_obj_annotated_text(text, parse_err, paramname) local obj = {} if text:find(":[^ ]") or text:find("%[%[") then local display, is_wikipedia_wikisource obj.text, obj.lang, obj.link, display, is_wikipedia_wikisource = parse_term_with_lang { term = text, parse_err = parse_err, paramname = paramname } -- HACK: If object is a Wikipedia or Wikisource link, we need to convert it to a two-part link -- to avoid the w: or s: prefix showing. We should probably avoid this by calling -- language_link() (in [[Module:links]]) in format_annotated_text() instead of the hacky stuff -- that we currently do; but we need to test that carefully to make sure it doesn't do things like -- munge % signs. if is_wikipedia_wikisource and not obj.text:find("%[%[") and not obj.text:find("%]%]") then -- Don't directly set .alt, because then if the user specifies <alt:...>, it will cause an error in -- parse_inline_modifiers(). obj.display = display end else obj.text = text obj.link = text end return obj end --[=[ Parse a textual property that may be in a foreign language or script and may be annotated with a language prefix and/or inline modifiers. `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...> inline modifier. If `val` is nil, the return value of this function is nil. Otherwise it is parsed for a language prefix (e.g. 'ar:مُؤَلِّف') and inline modifiers (e.g. 'ar:مُؤَلِّف<t:Author>'), and the return value is an object with the following fields: `lang`: The language object corresponding to the language prefix, if specified, or nil if no language prefix is given. `text`: The text after stripping off any language prefix and inline modifiers. `link`: The link part of the text if it consists of a two-part link; otherwise, same as `text`. `alt`: Display text specified using the <alt:...> modifier, if given; otherwise, nil. `subst`: Substitutions used to generate the transliteration, in the same format as the subst= parameter. `sc`: The script object corresponding to the <sc:...> modifier, if given; otherwise nil. `tr`: The transliteration corresponding to the <tr:...> modifier, if given; otherwise nil. `ts`: The transcription corresponding to the <ts:...> modifier, if given; otherwise nil. `gloss`: The gloss/translation corresponding to the `explicit_gloss` parameter (if given and non-nil), otherwise the <t:...> or <gloss:...> modifiers if given, otherwise nil. `f`: Foreign versions of the text. `q`: Left qualifiers. `qq`: Right qualifiers. Note that as a special case, if `val` contains HTML tags at the top level (e.g. '<span class="Arab">...</span>', as might be generated by specifying {{lang|ar|مُؤَلِّف}}), no language prefix or inline modifiers are parsed, and the return value has the `noscript` field set to true, which tells format_annotated_text() not to try to identify the script of the text and CSS-tag the text accordingly, but to leave the text untagged. This object can be passed to format_annotated_text() to format a string displaying the text (appropriately script-tagged, unless `noscript` is set, as described above) and modifiers. ]=] local function parse_annotated_text(val, fullname, explicit_gloss) if not val then return nil end -- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, caused -- by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed as having -- language code prefixes. See val_should_not_be_parsed_for_annotations() for more information. If we find a -- parameter value with top-level HTML in it, add 'noscript = true' to indicate that we should not try to do script -- inference and tagging. (Otherwise, e.g. if you specify {{lang|ar|مُؤَلِّف}} as the author, you'll get an extra big -- font coming from the fact that {{lang|...}} wraps the Arabic text in CSS that increases the size from the -- default, and then we do script detection and again wrap the text in the same CSS, which increases the size even -- more.) if val_should_not_be_parsed_for_annotations(val) then return {text = val, link = val, noscript = true, gloss = explicit_gloss} end local obj if val:find("<") then -- Check for inline modifier. obj = parse_inline_modifiers(val, { paramname = fullname, param_mods = param_mods, generate_obj = generate_obj_annotated_text, }) else obj = generate_obj_annotated_text(val, nil, fullname) end if explicit_gloss then obj.gloss = explicit_gloss end return obj end local html_entity_char_to_replacement = { ["<"] = TEMP_LT, [">"] = TEMP_GT, ["["] = TEMP_LBRAC, ["]"] = TEMP_RBRAC, } local function html_entity_replacement(entity, code_without_semicolon, hash, xcode, x, code) -- Try to decode the entity. If successful, Replace certain special HTML entities (those that are bracket-like) -- with single Unicode characters; otherwise, replace the semicolon with a special character so it won't get -- interpreted as a delimiter. local ch = decode_entities(entity) if ch ~= entity then return html_entity_char_to_replacement[ch] or code_without_semicolon .. TEMP_SEMICOLON end -- If the entity doesn't decode, escape it anyway iff it follows a valid format. if hash == "" then -- Any nonstandard MediaWiki-only entities have now been filtered off, so treat any non-ASCII characters as -- invalid. return xcode:match("^[^\128-\255]+$") and code_without_semicolon .. TEMP_SEMICOLON or entity elseif x == "" then return xcode:match("^%d+$") and code_without_semicolon .. TEMP_SEMICOLON or entity end return code:match("^%x+$") and code_without_semicolon .. TEMP_SEMICOLON or entity end local html_entity_replacement_to_char = { [TEMP_LT] = "&lt;", [TEMP_GT] = "&gt;", [TEMP_LBRAC] = "&#91;", [TEMP_RBRAC] = "&#93;", [TEMP_SEMICOLON] = ";", } local function undo_html_entity_replacement(txt) -- Pattern covers everything in html_entity_replacement_to_char. return (txt:gsub("\239\191[\177-\181]", html_entity_replacement_to_char)) end -- NOTE: We try hard to optimize this function for the common cases and avoid loading [[Module:parse utilities]] -- in such cases. The cases we can handle without loading [[Module:parse utilities]] are single values (no -- semicolons present) without inline modifiers or language prefixes, and multi-entity values (semicolons present) -- without (a) brackets of any kind (including parens, braces and angle brackets; angle brackets typically indicate -- inline modifiers and other brackets may protect a semicolon from being interpreted as a delimiter); -- (b) ampersands (which may indicate HTML entities, which protect a semicolon from being interpreted as a -- delimiter); and (c) colons not followed by a space (which may indicate a language prefix). local function generate_obj_multivalued_annotated_text(text, parse_err, paramname, no_undo_html_entity_replacement) local obj = generate_obj_annotated_text(text, parse_err, paramname) if not no_undo_html_entity_replacement then obj.text = undo_html_entity_replacement(obj.text) obj.link = undo_html_entity_replacement(obj.link) end return obj end --[=[ Similar to parse_annotated_text() but the parameter value may contain multiple semicolon-separated entities, each with their own inline modifiers. Some examples: * mainauthor=Paula Pattengale; Terea Sonsthagen * author=Katie Brick; J. Cody Nielsen; Greg Jao; Eric Paul Rogers; John A. Monson * author=Suzanne Brockmann; Patrick G. Lawlor (Patrick Girard); Melanie Ewbank * author=G Ristori; et al. * author=Jason Scott; zh:王晰宁<t:Wang Xining> * editors=zh:包文俊; zh:金心雯 * quotee=zh:張福運<t:Chang Fu-yun>; zh:張景文<t:Chang Ching-wen> There may be embedded semicolons within brackets, braces or parens that should not be treated as delimiters, e.g.: * author=Oliver Optic [pseudonym; {{w|William Taylor Adams}}] * author=author=Shannon Drake (pen name; {{w|Heather Graham Pozzessere}}) * author=James (the Elder;) Humphrys There may also be HTML entities with semicolons in them: * author=&#91;{{w|Gilbert Clerke}}&#93; * 2ndauthor=Martin Biddle &amp; Sally Badham * author=Peter Christen Asbj&oslash;rnsen There may be both embedded semicolons and HTML entities with semicolons in them: * author=&#91;{{w|Voltaire}} [pseudonym; François-Marie Arouet]&#93; In general we want to treat &#91; like an opening bracket and &#93; like a closing bracket. Beware that they may be mismatched: * author=Anonymous &#91;{{w|Karl Maria Kertbeny}}] Here, `val` is the value of the parameter and `fullname` is the name of the parameter from which the value was retrieved. `explicit_gloss`, if specified and non-nil, overrides any gloss specified using the <t:...> or <gloss:...> inline modifier, and `explicit_gloss_fullname` is the name of the parameter from which this value was retrieved. (If `explicit_gloss` is specified and multiple values were seen, an error results.) Return value is a list of objects of the same sort as returned by parse_annotated_text(). ]=] local function parse_multivalued_annotated_text(val, fullname, explicit_gloss, explicit_gloss_fullname) if not val then return nil end -- NOTE: In the code that follows, we use `entity` most of the time to refer to one of the semicolon-separated -- values in the multivalued param. Entities are most commonly people (typically authors, editors, translators or -- the like), but may be the names of publishers, locations, or other entities. "Entity" can also refer to HTML -- entities; in the places where this occurs, the variable name contains 'html' in it. local splitchar, english_delim if val:find("^,") then splitchar = "," english_delim = "comma" val = val:gsub("^,", "") else splitchar = ";" english_delim = "semicolon" end -- Optimization #1: No semicolons/commas or angle brackets (indicating inline modifiers). if not val:find("[<" .. splitchar .. "]") then if val_should_not_be_parsed_for_annotations(val) then return {{text = val, link = val, noscript = true}} else return {generate_obj_multivalued_annotated_text(val, nil, fullname, "no undo html entity replacement")} end end -- Optimization #2: Semicolons/commas but no angle brackets (indicating inline modifiers), braces, brackets, or -- parens (any of which would protect the semicolon/comma from interpretation as a delimiter), and no ampersand -- (which might indicate an HTML entity with a terminating semicolon, which should not be interpreted as a -- delimiter). if not val:find("[<>%[%](){}&]") then local entity_objs = {} for entity in gsplit(val, "%s*" .. splitchar .. "%s*") do if val_should_not_be_parsed_for_annotations(entity) then insert(entity_objs, { text = entity, link = entity, noscript = true }) else insert(entity_objs, generate_obj_multivalued_annotated_text(entity, nil, fullname, "no undo html entity replacement")) end end return entity_objs end -- Escape HTML entities, and get rid of directionality markers. local amp = val:find("&", nil, true) if amp then -- The pattern is more permissive than the usual entity pattern, as MediaWiki has some nonstandard entities -- that have non-ASCII characters in their codes. val = val:gsub("((&(#?)(([xX]?)([%w\128-\255]+)));)", html_entity_replacement) end -- Pattern covers left-to-right (U+200E) and right-to-left (U+200F). val = val:gsub("\226\128[\142\143]", "") -- Parse balanced segment runs, treating HTML entities for left and right bracket and left and right angle bracket -- as matching literal versions of the same characters. local entity_runs = parse_multi_delimiter_balanced_segment_run( val, {{"[" .. TEMP_LBRAC, "]" .. TEMP_RBRAC}, {"(", ")"}, {"{", "}"}, {"<" .. TEMP_LT, ">" .. TEMP_GT}}, true ) if type(entity_runs) == "string" then local undo_val = undo_html_entity_replacement(val) -- Parse error due to unbalanced delimiters. Don't throw an error here; instead, don't attempt to parse off -- any annotations, but return the value directly, maybe allowing script tagging (not allowing it if it appears -- the text is already script-tagged). return {{text = undo_val, link = undo_val, noscript = not not val_should_not_be_parsed_for_annotations(val)}} end -- Split on semicolon (or comma), possibly surrounded by whitespace. local separated_groups = split_alternating_runs(entity_runs, "%s*" .. splitchar .. "%s*") -- Process each value. local entity_objs = {} for _, entity_group in ipairs(separated_groups) do -- Rejoin runs that don't involve <...>. local j = 2 while j <= #entity_group do if not entity_group[j]:find("^<.*>$") then entity_group[j - 1] = entity_group[j - 1] .. entity_group[j] .. entity_group[j + 1] remove(entity_group, j) remove(entity_group, j) else j = j + 2 end end local oneval = undo_html_entity_replacement(concat(entity_group)) -- When checking for inline modifiers, exclude HTML entry with <span ...>, <i ...>, <br/> or similar in it, -- caused by wrapping an argument in {{l|...}}, {{lang|...}} or similar. Also exclude URL's from being parsed -- as having language code prefixes. This works analogously to parse_annotated_text(); see there for more. if val_should_not_be_parsed_for_annotations(oneval) then insert(entity_objs, { text = oneval, link = oneval, noscript = true }) else local obj if #entity_group > 1 then -- Check for inline modifier. obj = parse_inline_modifiers_from_segments({ group = entity_group, arg = oneval, props = { paramname = fullname, param_mods = param_mods, generate_obj = generate_obj_multivalued_annotated_text, }, }) else obj = generate_obj_multivalued_annotated_text(entity_group[1], nil, fullname) end insert(entity_objs, obj) end end if explicit_gloss then if #entity_objs > 1 then error( ( "Can't specify |%s= along with multiple %s-separated entities in |%s=; use the <t:...> " .. "inline modifier attached to the individual entities" ):format(explicit_gloss_fullname, english_delim, fullname) ) end entity_objs[1].gloss = explicit_gloss end return entity_objs end --[=[ Format a text property that may be in a foreign language or script, along with annotations. This is conceptually similar to the full_link() function in [[Module:links]], but displays the annotations in a different format that is more appropriate for bibliographic entries. The output looks like this: TEXT [TRANSLIT /TRANSCRIPTION/, GLOSS] `textobj` is as returned by parse_annotated_text(). `tag_text_func`, if supplied, is a function of one argument to further wrap the text after it has been processed and CSS-tagged appropriately, directly before insertion. `tag_gloss_func` is a similar function for the gloss. ]=] local function format_annotated_text(textobj, tag_text_func, tag_gloss_func) if not textobj then return nil end local text, link = textobj.text, textobj.link local subst, tr, ts, f, gloss = textobj.subst, textobj.tr, textobj.ts, textobj.f, textobj.gloss -- Retrieve the display text, either specified by the user (.alt) or by parse_annotated_text() (.display) when a -- Wikipedia link is given. local alt = textobj.alt or textobj.display if alt then if link:find("%[%[") or link:find("%]%]") then local errmsg = ("Can't currently handle embedded links in '%s', with <alt:...> text '%s'"):format(link, alt) error(escape_wikicode(errmsg)) end text = ("[[%s|%s]]"):format(link, alt) end -- See above for `noscript`, meaning HTML was found in the text value, probably generated using {{lang|...}}. -- {{lang}} already script-tags the text and processes embedded language links, so we don't want to do it again (in -- fact, the code below within the if-clause is similar to what {{lang}} does). In such a case, an explicit language -- won't be available and find_best_script_without_lang() may not be accurate, so we can't do automatic transliteration. if not textobj.noscript then local lang = textobj.lang -- As an optimization, don't do script detection on an argument that contains only ASCII. local sc = textobj.sc or lang and lang:findBestScript(text) or not text:find("^[ -~]$") and find_best_script_without_lang(text) or nil -- As an optimization, don't do any of the following if there's no language, script, translit or transcription, -- as will be the case with simple ASCII values. if lang or sc or tr or ts then if not lang then lang = get_lang("und") end if tr == "-" then tr = nil elseif not tr and sc and not sc:getCode():find("Lat") then -- Latn, Latf, Latg, pjt-Latn -- might return nil local text_for_tr = text if subst then text_for_tr = apply_subst(text_for_tr, subst) else text_for_tr = remove_links(text) end tr = (lang:transliterate(text_for_tr, sc)) end if text:find("%[%[") then -- FIXME: embedded_language_links() replaces % signs with their URL-encoded equivalents, -- which messes up URL's that may be present (e.g. if chapterurl= is given). IMO this -- should not happen, and embedded_language_links() should do nothing if no embedded links -- are present. To work around this, only call embedded_language_links() when there are -- embedded links present. text = embedded_language_links({ term = text, lang = lang, sc = sc, }) end if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then text = tag_text(text, lang, sc) end if tr then -- Should we link to the transliteration of languages with lang:link_tr()? Probably not because `text` is not -- likely to be a term that has an entry. tr = tag_translit(tr, lang, "usex") end if ts then ts = tag_transcription(ts, lang, "usex") end end end text = unitalicize_brackets(text) if tag_text_func then text = tag_text_func(text) end local parts = {} if textobj.q then insert(parts, format_qualifier(textobj.q) .. " ") end insert(parts, text) if tr or ts or f or gloss then insert(parts, SPACE_LBRAC) local subparts = {} if tr or ts then local tr_ts if ts then ts = "/" .. ts .. "/" end if tr and ts then tr_ts = tr .. " " .. ts else tr_ts = tr or ts end insert(subparts, tr_ts) end if f then for _, ff in ipairs(f) do local sc = ff.sc local lang if not sc and ff.quals then local qual = ff.quals[1] if type(qual) == "string" then -- do nothing; we'll do script detection farther down elseif qual:hasType("script") then sc = qual else -- language sc = qual:findBestScript(ff.val) lang = qual end end if not lang then lang = get_lang("und") end sc = sc or find_best_script_without_lang(ff.val) local val = embedded_language_links({ term = ff.val, lang = lang, sc = sc, }) if lang:getCode() ~= "und" or sc:getCode() ~= "Latn" then val = tag_text(val, lang, sc) end local qual_prefix if ff.quals then for i, qual in ipairs(ff.quals) do if type(qual) ~= "string" and (qual:hasType("script") or qual:hasType("language")) then ff.quals[i] = qual:getCanonicalName() end end qual_prefix = concat(ff.quals, "/") .. ": " else qual_prefix = "" end insert(subparts, qual_prefix .. val) end end if gloss then gloss = '<span class="e-translation">' .. gloss .. "</span>" gloss = unitalicize_brackets(gloss) if tag_gloss_func then gloss = tag_gloss_func(gloss) end insert(subparts, gloss) end insert(parts, concat(subparts, ", ")) insert(parts, RBRAC) end if textobj.qq then insert(parts, " " .. format_qualifier(textobj.qq)) end return concat(parts) end --[=[ Format a multivalued text property that may be in a foreign language or script, along with annotations. This is the multivalued analog to format_annotated_text(), and formats each individual entity using format_annotated_text(), joining the results with `delimiter`, which defaults to ", ". It `delimiter` is "and" or "or", join the results using serial_comma_join() with the specified conjunction. `textobjs` is as returned by parse_multivalued_annotated_text(). `tag_text_func` and `tag_gloss_func` are as in format_annotated_text(). ]=] local function format_multivalued_annotated_text(textobjs, delimiter, tag_text_func, tag_gloss_func) if not textobjs then return nil end if #textobjs == 1 then return format_annotated_text(textobjs[1], tag_text_func, tag_gloss_func) end local parts = {} for _, textobj in ipairs(textobjs) do insert(parts, format_annotated_text(textobj, tag_text_func, tag_gloss_func)) end -- Change delimiter to semicolon if the items themselves contain commas (e.g., in ", Jr.") local use_semicolon = false for _, obj in ipairs(textobjs) do -- Use remove_links to ignore commas in links if obj.text and remove_links(obj.text):find(", ") then use_semicolon = true break end end local n = #parts if n > 0 and parts[n]:match("^'*et al[.']*$") then -- Special handling for 'et al.' parts[n] = "''et al.''" if n == 2 then -- author et al. return concat(parts, " ") else -- author 1, author 2, et al. return concat(parts, (use_semicolon and "; ") or ", ") end if delimiter == "and" or delimiter == "or" then delimiter = ", " end return concat(parts, delimiter) end if delimiter == "and" or delimiter == "or" then return serial_comma_join(parts, {conj = delimiter}) end return concat(parts, delimiter or ((use_semicolon and "; ") or ", ")) end -- Fancy version of ine() (if-not-empty). Converts empty string to nil, but also strips leading/trailing space. local function ine(arg) if not arg then return nil elseif type(arg) ~= "string" then return arg end arg = mw.text.trim(arg) if arg == "" then return nil end return arg end local abbrs = { ["a."] = {anchor = "a.", full = "ante"}, ["c."] = {anchor = "c.", full = "circa"}, ["p."] = {anchor = "p.", full = "post"}, } -- Process prefixes 'a.' (ante), 'c.' (circa) and 'p.' (post) at the beginning of an arbitrary date or year spec. -- Returns two values, the formatted version of the prefix and the date spec minus the prefix. If no prefix is found, -- returns an empty string and the full date. local function process_ante_circa_post(date) local prefix = usub(date, 1, 2) local abbr = abbrs[prefix] local abbr_prefix = "" if abbr then abbr_prefix = "''[[Appendix:Glossary#" .. abbr.anchor .. '|<abbr title="' .. abbr.full .. '">' .. abbr.anchor .. "</abbr>]]'' " -- Remove lowercase letter, period, and space from beginning of date parameter. date = ugsub(date, "^%l%.%s*", "") end return abbr_prefix, date end -- Format the arguments that specify the date of the quotation. These include the following: -- |date=: The date. If |start_date= is given, this is the end date. -- |year=, |month=: Year and month of quotation date or end of range, if |date= isn't given. -- |start_date=: The start date, to specify a range. -- |start_year=, |start_month=: Year and month of start of range, if |start_date= isn't given. -- |accessdate=: Date a website was accessed; processed if no other date was given. -- |nodate=: Indicate that no date is present; otherwise a maintenance line will be displayed if there is no date. -- -- If `parampref` and/or `paramsuf` are given, this modifies all the date arguments accordingly. For example, if -- `parampref` == "orig" and `paramsuf` is omitted, the date is specified using |origdate= or |origyear=/|origmonth=, -- and the start of the range is |origstart_date=, etc. Similarly, if `parampref` is omitted and `paramsuf` is -- "_published", the date is specified using |date_published= or |year_published=/|month_published=, and the start of -- the range is |start_date_published=, etc. -- -- `a` and `get_full_paramname` are functions with the same interpretation as the local functions of the same name in -- source(). These are used to fetch parameters and get their full names. Note that this may cause all arguments to -- have an index added to them (|date2=, |year2=, |month2=, etc.). -- -- `alias_map` is as in source() and is used to map canonical arguments to their aliases when aliases were used. -- -- If `bold_year` is given, displayed years are boldfaced unless boldface is present in the parameter value. -- -- If `maintenance_line_no_date` is specified, it should be a string that will be returned if no date is found (i.e. -- neither |date= nor |year=, or their appropriate equivalents per `parampref` and `paramsuf`, are specified, and -- neither |nodate= is given to indicate that there is no date, or |accessdate= is given). -- -- Returns two values: the formatted date and a boolean indicating whether to add a maintenance category -- [[:Category:Requests for date in LANG entries]]. The first return value will be nil if nothing is to be added -- (in which case the scond return value will always be nil). local function format_date_args( a, get_full_paramname, alias_map, parampref, paramsuf, bold_year, maintenance_line_no_date, year_last ) local output = {} parampref = parampref or "" paramsuf = paramsuf or "" local function getp(param) return a(parampref .. param .. paramsuf) end local function pname(param) local fullname = get_full_paramname(parampref .. param .. paramsuf) return alias_map[fullname] or fullname end -- Format `timestamp` (a timestamp referencing a date) according to the spec in `code`. `param` is the base name of -- the parameter from which the timestamp was fetched, for error messages. local function format_date_with_code(code, timestamp, param) local language = mw.getContentLanguage() local ok, date = pcall(language.formatDate, language, code, timestamp) if ok then return date else -- All the formats used in format_date_args() are fine, so the timestamp must be at fault. error( ( "Timestamp |%s=%s (possibly canonicalized from its original format) could not be parsed; see the " .. "[[mw:Help:Extension:ParserFunctions##time|documentation for the #time parser function]]" ):format(pname(param), tostring(timestamp)) ) end end -- Try to figure out if the given timestamp has the day of the month explicitly given. We use the following -- algorithm: -- 1. Format as year-month-day; if the day is not 1, the day was explicitly given, since if only the year/month are -- given, the day shows up as 1. -- 2. If the day shows up as 1 and there isn't a 1 or 01 in the timestamp, the day wasn't explicitly given. -- 3. Otherwise, if there are three separate numbers (e.g. 2022-07-01), or two separate numbers plus a capitalized -- letter (taken as an English month, e.g. 2022 July 1), the day was explicitly given, otherwise not. -- -- `param` is the base name of the parameter from which the timestamp was fetched. local function date_has_day_specified(timestamp, param) local day = format_date_with_code("j", timestamp, param) if day ~= "1" then return true end local english_month = timestamp:find("%u") local canon_timestamp = mw.text.trim((timestamp:gsub("%D+", " "))) local seen_nums = split(canon_timestamp, " ", true) local saw_one = false for _, num in ipairs(seen_nums) do if num == "1" or num == "01" then saw_one = true break end end if not saw_one then return false end return #seen_nums >= 3 or english_month and #seen_nums >= 2 end -- Format a date with boldfaced year, as e.g. '''2023''' August 3. `explicit_day_given` indicates whether to include -- the day; if false, the return value will be e.g. '''2023''' August. `date_param` is the base name of the param -- from which the date was fetched, for error messages. local function format_bold_date(date, explicit_day_given, date_param) local day_month_code = explicit_day_given and "j F" or "F" local month_day_code = explicit_day_given and "F j" or "F" if year_last then if bold_year then -- This formats like "3 August '''2023'''" (or "August '''2023'''" if day not explicitly given). return format_date_with_code(day_month_code .. " '''Y'''", date, date_param) else -- This formats like "3 August 2023" (or "August 2023" if day not explicitly given). return format_date_with_code(day_month_code .. " Y", date, date_param) end else if bold_year then -- This formats like "'''2023''' August 3" (or "'''2023''' August" if day not explicitly given). return format_date_with_code("'''Y''' " .. month_day_code, date, date_param) else -- This formats like "2023 August 3" (or "2023 August" if day not explicitly given). return format_date_with_code("Y " .. month_day_code, date, date_param) end end end -- The formatDate method of the mw.language object behaves like the {{#time:}} parser function, which doesn't -- accept the formats "monthday monthname, year" or "year monthname monthday", but outputs garbage when it receives -- them, behavior inherited from PHP. {{#formatdate:}} magic word is more forgiving. Fix dates so that, for -- instance, the |date= parameter of {{quote-journal}} (which uses this code) and the |accessdate= parameter (which -- uses {{#formatdate:}}) accept similar date formats. See: -- * [[mw:Extension:Scribunto/Lua_reference_manual#mw.language:formatDate]] -- * [[mw:Help:Extension:ParserFunctions##time]] -- * [[mw:Help:Magic_words#Formatting]] -- `date` is the date spec from the user, which is assumed to come from a parameter whose base name ends in "date"; -- `parampref` is the prefix added to "date" to get the parameter name. local function fix_date(date, param_pref) if tonumber(date) ~= nil then error( ("|%s= should contain a full date (year, month, day of month); use |%s= for year"):format( pname(param_pref .. "date"), pname(param_pref .. "year") ) ) elseif date and date:find("%s*%a+,%s*%d+%s*$") then error( ("|%s= should contain a full date (year, month, day of month); use |%s=, |%s= for month and year"):format( pname(param_pref .. "date"), pname(param_pref .. "month"), pname(param_pref .. "year") ) ) end if date then -- Commas are replaced with spaces to prevent parsing issues local fixed_date = ugsub(date, ", *", " ") return (ugsub(ugsub(fixed_date or date, "(%d+ %a+),", "%1"), "^(%d%d%d%d) (%a+ %d%d?)$", "%2 %1")) end end local start_date, date = fix_date(getp("start_date"), "start_"), fix_date(getp("date"), "") local year = getp("year") local month = getp("month") local start_year = getp("start_year") local start_month = getp("start_month") if date and year then error(("Only one of |%s= or |%s= should be specified"):format(pname("date"), pname("year"))) end if date and month then error( ("|%s= should only be specified in conjunction with |%s=, not with |%s="):format( pname("month"), pname("year"), pname("date") ) ) end if start_date and start_year then error(("Only one of |%s= or |%s= should be specified"):format(pname("start_date"), pname("start_year"))) end if start_date and start_month then error( ("|%s= should only be specified in conjunction with |%s=, not with |%s="):format( pname("start_month"), pname("start_year"), pname("start_date") ) ) end if (start_date or start_year) and not (date or year) then error( ("|%s= or |%s=/|%s= cannot be specified without specifying |%s= or |%s=/|%s="):format( pname("start_date"), pname("start_year"), pname("start_month"), pname("date"), pname("year"), pname("month") ) ) end local dash = "&nbsp;– " local day_explicitly_given = date and date_has_day_specified(date, "date") local start_day_explicitly_given = start_date and date_has_day_specified(start_date, "start_date") -- Format a date with boldfaced year, as e.g. '''2023''' August 3 (if `explicit_day_given` specified) or -- '''2023''' August (if `explicit_day_given` not specified). If no date specified, fall back to formatting based -- on the year and (optionally) month params given in `yearobj` and `monthobj`, boldfacing the year if not already. -- `date_param` is the base name of the param from which the date was fetched, for error messages. local function format_date_or_year_month(date, yearobj, monthobj, explicit_day_given, date_param) if date then return format_bold_date(date, explicit_day_given, date_param) else -- Boldface a year spec if it's not already boldface. if bold_year and not yearobj.text:find("'''") then -- Clone the year object before modifying it because we may use it later to check against the current -- year (if we're dealing with start_year). yearobj = shallow_copy(yearobj) yearobj.text = "'''" .. yearobj.text .. "'''" if yearobj.alt then yearobj.alt = "'''" .. yearobj.alt .. "'''" end end if year_last then return (monthobj and format_annotated_text(monthobj) .. " " or "") .. format_annotated_text(yearobj) else return format_annotated_text(yearobj) .. (monthobj and " " .. format_annotated_text(monthobj) or "") end end end local yearobj = parse_annotated_text(year, pname("year")) local monthobj = parse_annotated_text(month, pname("month")) local start_yearobj = parse_annotated_text(start_year, pname("start_year")) local start_monthobj = parse_annotated_text(start_month, pname("start_month")) if start_yearobj then local abbr_prefix abbr_prefix, start_yearobj.text = process_ante_circa_post(start_yearobj.text) start_yearobj.link = select(2, process_ante_circa_post(start_yearobj.link)) insert(output, abbr_prefix) elseif yearobj then local abbr_prefix abbr_prefix, yearobj.text = process_ante_circa_post(yearobj.text) yearobj.link = select(2, process_ante_circa_post(yearobj.link)) insert(output, abbr_prefix) end if start_date or start_year then local cur_year = yearobj and yearobj.text or format_date_with_code("Y", date, "date") local cur_month = monthobj and monthobj.text or date and format_date_with_code("F", date, "date") or nil local cur_day = date and day_explicitly_given and format_date_with_code("j", date, "date") or nil local beg_year = start_yearobj and start_yearobj.text or format_date_with_code("Y", start_date, "start_date") local beg_month = start_monthobj and start_monthobj.text or start_date and format_date_with_code("F", start_date, "start_date") or nil local beg_day = start_date and start_day_explicitly_given and format_date_with_code("j", start_date, "start_date") or nil if year_last then if beg_day then insert(output, beg_day .. " " .. beg_month) else insert(output, beg_month) end if beg_year ~= cur_year then insert(output, " " .. beg_year) end else insert(output, format_date_or_year_month( start_date, start_yearobj, start_monthobj, start_day_explicitly_given, "start_date" )) end if cur_year ~= beg_year then -- Different years; insert current date in full. if beg_month or cur_month then insert(output, dash) else insert(output, "–") end insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date")) elseif cur_month and cur_month ~= beg_month then local month_ins = monthobj and format_annotated_text(monthobj) or cur_month -- Same year but different months; insert current month and (if available) current day. if cur_day then insert(output, dash) if year_last then insert(output, cur_day .. " " .. month_ins) else insert(output, month_ins .. " " .. cur_day) end else if beg_day then insert(output, dash) else insert(output, "–") end insert(output, month_ins) end elseif cur_day and cur_day ~= beg_day then -- Same year and month but different days; insert current day. insert(output, "–") insert(output, cur_day) else -- Same year, month and day; or same year and month, and day not available; or same year, and month and -- day not available. Do nothing. FIXME: Should we throw an error? end if year_last and beg_year == cur_year then if cur_month then insert(output, " " .. cur_year) else insert(output, " " .. cur_year) end end elseif date or yearobj then insert(output, format_date_or_year_month(date, yearobj, monthobj, day_explicitly_given, "date")) elseif not maintenance_line_no_date then -- Not main quote date. Return nil, caller will handle. return nil, nil elseif not getp("nodate") then local accessdate = getp("accessdate") if accessdate then local explicit_day_given = date_has_day_specified(accessdate, "accessdate") insert(output, format_bold_date(accessdate, explicit_day_given, "accessdate") .. " (last accessed)") else if mw.title.getCurrentTitle().namespace ~= 10 then return maintenance_line(maintenance_line_no_date), true end return nil, nil end end return ine(concat(output)), nil end local function tag_with_cite(txt) return "<cite>" .. txt .. "</cite>" end -- Display the source line of the quote, above the actual quote text. This contains the majority of the logic of this -- module (formerly contained in {{quote-meta/source}}). function export.source(args, alias_map, format_as_cite, other_controls) local tracking_categories = {} local argslang = args[1] or args.lang if not argslang then -- For the moment, only trigger an error on mainspace pages and -- other pages that are not user pages or pages containing discussions. -- These are the same pages that appear in the appropriate tracking -- categories. User and discussion pages have not generally been -- fixed up to include a language code and so it's more helpful -- to use a maintenance line than signal an error. local current_title = mw.title.getCurrentTitle() if not (current_title.namespace == 10 or page_should_be_ignored(current_title.fullText)) then require(languages_error_module)(nil, 1) end end -- Given a canonical param, convert it to the original parameter specified by the user (which may have been an -- alias). local function alias(param) return alias_map[param] or param end -- Wrap contributor names (authors, editors, translators, compilers, etc.) and the bolded publication year in a -- span so they can be styled distinctly (e.g. Kurdish Wiktionary's "j-pewist" highlighting). local function j_lazim(text) if not text then return nil end return '<span class="j-pewist">' .. text .. "</span>" end local output, sep = {} local overrides = other_controls and other_controls.overrides or {} -- Add text to the output. The text goes into a list, and we concatenate all the list components together at the -- end. To make it easier to handle comma-separated items, we keep track (in `sep`) of the separator (if any) that -- needs to be inserted before the next item added. For example, if we're in the "newversion" code (ind ~= ""), and -- there's no title and no URL, then the first time we add anything after the title, we don't want to add a -- separating comma because the preceding text will say "republished " or "republished as " or "translated as " or -- similar. In all- other cases, we do want to add a separating comma. The bare add() function reset the separator -- to be nothing, while the add_with_sep() function resets the separator to be the value of `next_sep` (defaulting -- to ", "), so the next time around we do add a comma to separate `text` from the preceding piece of text. local function add(text) if sep then insert(output, sep) end insert(output, text) sep = nil end local function add_with_sep(text, next_sep) add(text) sep = next_sep or ", " end -- FIXME: This is all very over-engineered, which makes it slow and memory-inefficient. -- Return a function that generates the actual parameter name associated with a base param (e.g. "author", "last"). -- The actual parameter name may have an index added (an empty string for the first set of params, e.g. author=, -- last=, or a numeric index for further sets of params, e.g. author2=, last2=, etc.). local function make_get_full_paramname(ind) return function(param) return param .. ind end end -- Function to fetch the actual parameter name associated with a base param (see make_get_full_paramname() above). -- Assigned at various times below by calling make_get_full_paramname(). We do it this way so that we can have -- wrapper functions that access params and define them only once. local get_full_paramname -- Return two values: the value of a parameter given the base param name (which may have a numeric index added), -- and the parameter name from which the value was fetched (which may be an alias, i.e. you can't necessarily fetch -- the parameter value from args[] given this name). The base parameter can be a list of such base params, which -- are checked in turn, or nil, in which case nil is returned. local function a_with_name(param) if not param then return nil elseif type(param) ~= "table" then local fullname = get_full_paramname(param) return args[fullname], alias(fullname) end for _, par in ipairs(param) do local val, fullname = a_with_name(par) if val then return val, alias(fullname) end end return nil end -- Fetch the value of a parameter given the base param name (which may have a numeric index added). The base -- parameter can be a list of such base params, which are checked in turn, or nil, in which case nil is returned. local function a(param) return (a_with_name(param)) end -- Identical to a_with_name(param) except that it verifies that no space is present. Should be used for URL's. local function aurl_with_name(param) local value, fullname = a_with_name(param) if value and value:find(" ") and not value:find("%[") then error(("URL not allowed to contain a space, but saw |%s=%s"):format(fullname, value)) end return value, fullname end -- Identical to a(param) except that it verifies that no space is present. Should be used for URL's. local function aurl(param) return (aurl_with_name(param)) end -- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have -- a language prefix and/or inline modifiers), parse the annotations and convert the result into a formatted string. -- This is the same as parse_and_format_annotated_text() below but also returns the full param name as the second -- return value. local function parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func) local val, fullname = a_with_name(param) local obj = parse_annotated_text(val, fullname) return format_annotated_text(obj, tag_text_func, tag_gloss_func), fullname end -- Convenience function to fetch a parameter that may be in a foreign language or text (and may consequently have -- a language prefix and/or inline modifiers), parse the modifiers and convert the result into a formatted string. -- This is a wrapper around parse_annotated_text() and format_annotated_text(). `param` is the base parameter name (see -- a_with_name()), `tag_text_func` is an optional function to tag the parameter text after all other processing (e.g. -- wrap in <cite>...</cite> tags), and `tag_gloss_func` is a similar function for the parameter translation/gloss. local function parse_and_format_annotated_text(param, tag_text_func, tag_gloss_func) return (parse_and_format_annotated_text_with_name(param, tag_text_func, tag_gloss_func)) end -- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may -- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a -- formatted string. This is the multivalued analog to parse_and_format_annotated_text_with_name() and returns two -- values, the formatted string and the full name of the parameter fetched. `delimiter` is as in -- format_multivalued_annotated_text(). local function parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func) local val, fullname = a_with_name(param) local objs = parse_multivalued_annotated_text(val, fullname) local num_objs = objs and #objs or 0 return format_multivalued_annotated_text(objs, delimiter, tag_text_func, tag_gloss_func), fullname, num_objs end -- Convenience function to fetch a multivalued parameter that may be in a foreign language or text (and may -- consequently have a language prefix and/or inline modifiers), parse the modifiers and convert the result into a -- formatted string. This is the multivalued analog to parse_and_format_annotated_text(). `delimiter` is as in -- format_multivalued_annotated_text(). local function parse_and_format_multivalued_annotated_text(param, delimiter, tag_text_func, tag_gloss_func) return (parse_and_format_multivalued_annotated_text_with_name(param, delimiter, tag_text_func, tag_gloss_func)) end -- This determines whether to display "Mary Bloggs, transl." (if there's no author preceding) or "translated by -- Mary Bloggs" (if there's an author preceding). local author_outputted = false -- When formatting as a citation, the priority is to display a name and a date before the book/chapter title -- this tracks whether or not the author/date has been displayed local date_outputted, formatted_date, formatted_origdate = false local function add_date(no_paren) if not date_outputted then if no_paren then sep = ", " else sep = " " end if formatted_date then if no_paren then add(formatted_date) else add("(" .. formatted_date .. ")") end end if formatted_origdate then add(SPACE_LBRAC .. formatted_origdate .. RBRAC) end sep = ", " date_outputted = true end end local function is_anonymous(val) return val:match("^[Aa]non[yî]mo?u?s?$") or val:match("^[Aa]non%.?$") end -- Add a formatted author (whose values may be specified using `author` or, for compatibility purposes, split -- among various parameters): -- * `author` is the value of the author param (e.g. "author", "author2" or "2ndauthor"), and `author_fullname` is -- the full parameter name holding that value; -- * `trans_author` is the optional value of the param holding the gloss/translation of the author, and -- `trans_author_fullname` is the full parameter name holding that value (or nil for no such parameter); -- * `authorlink` is the value of the authorlink param, which holds the Wikipedia link of the author(s) in `author`, -- and `authorlink_fullname` is the full parameter name holding that value; -- * `trans_authorlink` is the optional value of the param holding the Wikipedia link of the gloss/translation of -- the author, and `trans_authorlink_fullname` is the full parameter name holding that value (or nil for no such -- parameter); -- * `first` is the value of the parameter holding the first name of the author, and `first_fullname` is the full -- parameter name holding that value; -- * `trans_first` is the value of the corresponding parameter holding the gloss/translation of the first name -- (e.g. "trans-first"), and `trans_first_fullname` is the full parameter name holding that value (or nil for -- no such parameter); -- * `last` is the value of the parameter holding the last name of the author, and `last_fullname` is the full -- parameter name holding that value; -- * `trans_last` is the value of the corresponding parameter holding the gloss/translation of the last name -- (e.g. "trans-last"), and `trans_last_fullname` is the full parameter name holding that value (or nil for -- no such parameter). -- * `last_first` if set, when parameters `first` and `last` are used, display the author name as "last, first" local function add_author( author, author_fullname, trans_author, trans_author_fullname, authorlink, authorlink_fullname, trans_authorlink, trans_authorlink_fullname, first, first_fullname, trans_first, trans_first_fullname, last, last_fullname, trans_last, trans_last_fullname, last_first ) local function make_author_with_url(txt, txtparam, authorlink, authorlink_param) if authorlink then if authorlink:find("%[%[") then error(("Can't specify links in |%s=%s"):format(authorlink_param, authorlink)) end if txt:find("%[%[") then error(("Can't specify links in %s=%s"):format(txtparam, txt)) end return "[[w:" .. authorlink .. "|" .. txt .. "]]" else return txt end end local num_authorobjs if author then local authorobjs = parse_multivalued_annotated_text(author, author_fullname, trans_author, trans_author_fullname) num_authorobjs = #authorobjs if num_authorobjs == 1 then if is_anonymous(authorobjs[1].text) then authorobjs[1].text = "nivîskarê anonîm" authorobjs[1].link = "nivîskarê anonîm" end if authorlink then authorobjs[1].text = make_author_with_url( authorobjs[1].text, "|" .. author_fullname, authorlink, "|" .. authorlink_fullname ) authorobjs[1].link = make_author_with_url( authorobjs[1].link, "|" .. author_fullname, authorlink, "|" .. authorlink_fullname ) end if authorobjs[1].gloss and trans_authorlink then authorobjs[1].gloss = make_author_with_url( authorobjs[1].gloss, ("<t:...> in |%s"):format(author_fullname), trans_authorlink, "|" .. trans_author_fullname ) end add(j_lazim(format_multivalued_annotated_text(authorobjs))) elseif trans_authorlink then error( ( "Can't specify |%s= along with multiple semicolon-separated entities in |%s=; use the " .. "<t:...> inline modifier attached to the individual entities and put the link directly " .. "in the value of the inline modifier" ):format(trans_authorlink_fullname, author_fullname) ) else -- Allow an authorlink with multiple authors, e.g. for use with |author=Max Mills; Harvey Mills -- with |authorlink=Max and Harvey. For this we have to generate the entire text and link it -- all. local formatted_text = format_multivalued_annotated_text(authorobjs) if authorlink then formatted_text = make_author_with_url( formatted_text, "|" .. author_fullname, authorlink, "|" .. authorlink_fullname ) end add(j_lazim(formatted_text)) end else num_authorobjs = 1 -- Author separated into first name + last name. We don't currently support non-Latin-script -- authors separated this way and probably never will. if first then if last_first then author = last .. ", " .. first else author = first .. " " .. last end else author = last end if authorlink then local authorparam = first and ("|%s |%s"):format(first_fullname, last_fullname) or "|" .. last_fullname author = make_author_with_url(author, authorparam, authorlink, authorlink_fullname) end local trans_author if trans_last then if trans_first then trans_author = trans_first .. " " .. trans_last else trans_author = trans_last end if trans_authorlink then local trans_authorparam = trans_first and ("|%s |%s"):format(trans_first_fullname, trans_last_fullname) or "|" .. trans_last_fullname trans_author = make_author_with_url( trans_author, trans_authorparam, trans_authorlink, trans_authorlink_fullname ) end end add(j_lazim(author)) if trans_author then add(SPACE_LBRAC) add(trans_author) add(RBRAC) end end author_outputted = true return num_authorobjs end local function add_authorlike( param, prefix_with_preceding_authors, suffix_without_preceding_authors, suffix_if_multiple, anonymous_suffix ) local delimiter = author_outputted and "and" or ", " local entities, _, num_entities = parse_and_format_multivalued_annotated_text_with_name(param, delimiter) if not entities then return end if is_anonymous(entities) then -- If tlr=anonymous or similar given, display as "anonymous translator" or similar. If a specific -- anonymous suffix not given, try to derive the anonymous suffix from the non-preceding-author suffix. if not anonymous_suffix then local cleaned_suffix = suffix_without_preceding_authors :gsub("&#32;", " ") :gsub("&nbsp;", " ") :gsub("&#160;", " ") :gsub("&#91;", "[") :gsub("&#93;", "]") cleaned_suffix = mw.text.trim(cleaned_suffix) if not anonymous_suffix then anonymous_suffix = " " .. cleaned_suffix:match("^, (.*)$") end if not anonymous_suffix then anonymous_suffix = " " .. cleaned_suffix:match("^%((.*)%)$") end if not anonymous_suffix then anonymous_suffix = " " .. cleaned_suffix:match("^%[(.*)%]$") end if not anonymous_suffix then anonymous_suffix = suffix_without_preceding_authors end end add_with_sep(j_lazim("anonîm" .. anonymous_suffix)) else entities = j_lazim(entities) if prefix_with_preceding_authors and (author_outputted or not suffix_without_preceding_authors) then add_with_sep(prefix_with_preceding_authors .. entities) elseif suffix_if_multiple and num_entities > 1 then add_with_sep(entities .. suffix_if_multiple) else add_with_sep(entities .. suffix_without_preceding_authors) end end author_outputted = true end local function add_authorlabel() local default_authorlabel = a("default-authorlabel") if default_authorlabel and yesno(a("authorlabel"), true) then sep = nil add_with_sep(" " .. default_authorlabel) end end local function has_new_title_or_author() return args["2ndauthor"] or args["2ndlast"] or args.chapter2 or args.title2 or args.tlr2 or args.mainauthor2 or args.editor2 or args.editors2 or args.compiler2 or args.compilers2 or args.director2 or args.directors2 end local function has_newversion() return args.newversion or args.location2 or has_new_title_or_author() end -- Handle chapter=, section=, etc. `param` is the base name of the parameter in question, e.g. "chapter" or -- "section". If numeric (either Arabic or Roman), add `numeric_prefix`; otherwise, parse as textual (allowing for -- language prefixes, inline modifiers, etc.), prefix with `textual_prefix` (if given) and suffix with -- `textual_suffix` (if given). Also checks for and handles the following (assuming param == "chapter"): -- * chapterurl=: URL of the chapter. -- * trans-chapter=: Chapter translation (can be given using an inline modifier <t:...>). -- * chapter_number=: Chapter number, when chapter= is also given (otherwise put the chapter number in chapter=). -- * chapter_plain=: Plain version of the chapter number; the "chapter " prefix isn't added. -- * chapter_series=: Series that the chapter is within (used e.g. for journal articles part of a series). -- * chapter_seriesvolume=: Volume of the series (compare seriesvolume=). -- -- Returns nil if no value specified for the main parameter, otherwise the formatted value. local function format_chapterlike(param, numeric_prefix, textual_prefix, textual_suffix) local chap, chap_fullname = a_with_name(param) local chap_num, chap_num_fullname = a_with_name(param .. "_number") local chap_plain, chap_plain_fullname = parse_and_format_annotated_text_with_name(param .. "_plain") if chap_num and chap_plain then error(("Specify only one of |%s= or %s="):format(chap_num_fullname, chap_plain_fullname)) end local chap_series, chap_series_fullname = parse_and_format_annotated_text_with_name(param .. "_series", tag_with_cite, tag_with_cite) local chap_seriesvolume, chap_seriesvolume_fullname = parse_and_format_annotated_text_with_name(param .. "_seriesvolume") if chap_series then chap_series = ", " .. chap_series end if chap_seriesvolume then if not chap_series then error(("Ji bo |%s= ev %s= lazim e"):format(chap_series_fullname, chap_seriesvolume_fullname)) end chap_series = chap_series .. " (" .. chap_seriesvolume .. ")" end local chapterurl local function make_chapter_with_url(chap) if chapterurl then return "[" .. chapterurl .. " " .. chap .. "]" else return chap end end if not chap then if chap_num then error( ("Ji bo |%s= ev |%s= lazim e; nirxa hejmarî li |%s= binivîse"):format( chap_num_fullname, chap_fullname, chap_fullname ) ) end if chap_plain then chapterurl = aurl(param .. "url") return make_chapter_with_url(chap_plain .. (chap_series or "")) end return nil end local cleaned_chap = chap:gsub("<sup>[^<>]*</sup>", ""):gsub("[*+#]", "") chapterurl = aurl(param .. "url") local formatted if numeric_prefix and get_number(cleaned_chap) then -- Arabic chapter number formatted = numeric_prefix .. make_chapter_with_url(chap) elseif numeric_prefix and cleaned_chap:match("^[mdclxviMDCLXVI]+$") and roman_to_arabic(cleaned_chap, true) and (not overrides[param] or not overrides[param].noroman) then -- Roman chapter number formatted = numeric_prefix .. make_chapter_with_url(upper(chap)) else -- strip leading ! (used to force text-mode for titles that look like Roman numerals like "mil" and "dill") if chap:find("^!") then chap = chap:gsub("^!", "") end -- Must be a chapter name local chapterobj = parse_annotated_text(chap, chap_fullname, a("trans-" .. param)) chapterobj.text = make_chapter_with_url(chapterobj.text) chapterobj.link = make_chapter_with_url(chapterobj.link) formatted = (textual_prefix or "") .. format_annotated_text(chapterobj) .. (textual_suffix or "") end if chap_num or chap_plain then -- NOTE: Up above we throw an error if both chap_num and chap_plain are specified. formatted = formatted .. " (" .. (chap_plain or numeric_prefix .. chap_num) .. ")" end if chap_series then formatted = formatted .. chap_series end return formatted end -- This handles everything after displaying the author, starting with the chapter and ending with page, column, -- line and then other=. It is currently called twice: Once to handle the main portion of the citation, and once to -- handle a "newversion" citation. `ind` is either "" for the main portion or a number (currently only 2) for a -- "newversion" citation. In a few places we conditionalize on `ind` to take actions depending on its value. local function postauthor(ind, num_authors, format_as_cite) get_full_paramname = make_get_full_paramname(ind) if author_outputted then add_authorlabel() end local coauthors = parse_and_format_multivalued_annotated_text("coauthors", "and") coauthors = j_lazim(coauthors) if coauthors then local with_prefix = "" if author_outputted then with_prefix = "li gel " if num_authors == 1 then sep = " " end end add_with_sep(with_prefix .. coauthors) author_outputted = true end add_authorlike("quotee", "quoting ", ", quotee", ", quotees") if format_as_cite and author_outputted and not date_outputted then add_date() sep = ", " end add_authorlike("chapter_tlr", "terciman: ", ", tercime", nil, " terciman") local function add_sg_and_pl_authorlike(noun, verbed) local sgparam = noun local plparam = noun .. "s" local sgval, sgval_fullname = a_with_name(sgparam) local plval, plval_fullname = a_with_name(plparam) if sgval and plval then error(("Nabe ku hem |%s= hem jî |%s= were bikaranîn"):format(sgval_fullname, plval_fullname)) end if sgval or plval then local verbed_by = verbed .. " ji hêla " local comma_sgnoun = ", " .. noun local comma_plnoun = ", " .. noun .. "s" add_authorlike(sgparam, verbed_by, comma_sgnoun, comma_plnoun) add_authorlike(plparam, verbed_by, comma_plnoun) end end local formatted_entry = format_chapterlike("entry", nil, "“", "”") local formatted_chapter = format_chapterlike("chapter", "chapter ", "“", "”") local function add_entry() if formatted_entry then add_with_sep(formatted_entry) if not a("notitle") then add("li ser ") author_outputted = false else author_outputted = true end formatted_entry = nil end end local function add_chapter() add_entry() if formatted_chapter then add_with_sep(formatted_chapter) if not a("notitle") then add("li ser ") author_outputted = false end formatted_chapter = nil end end local function add_actor_role(format_as_cite) local role = parse_and_format_multivalued_annotated_text("role", "and") role = j_lazim(role) local actor_val, actor_fullname = a_with_name("actor") local actor_objs = parse_multivalued_annotated_text(actor_val, actor_fullname) local actor = format_multivalued_annotated_text(actor_objs, "and") actor = j_lazim(actor) if format_as_cite then if role then if actor then add_with_sep(actor .. " wekî ") end sep = nil add_with_sep(role) elseif actor then add_with_sep(actor .. " (aktor)") end else if role then add_with_sep("xeberdan ji aliyê " .. role .. " ve") if actor then sep = nil add_with_sep(" (" .. actor .. ")") end elseif actor then add_with_sep(actor .. " (aktor)") end end end if format_as_cite then if date_outputted then add_chapter() end local output_len = #output local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor") mainauthor = j_lazim(mainauthor) if mainauthor then add_with_sep(mainauthor) end -- quote-* templates display "jobbed by name" after the author, controlled by the author_outputted flag author_outputted = false add_authorlike("tlr", "terciman: ", ", tercime", nil, " terciman") author_outputted = false add_sg_and_pl_authorlike("editor", "edîtor") add_sg_and_pl_authorlike("compiler", "berhevkar") add_sg_and_pl_authorlike("director", "dîrektor") add_authorlike("lyricist", nil, " (gotin)", nil, " gotinnivîs") add_authorlike("lyrics-translator", nil, " (tercime)", nil, " tercimenê gotinan") add_authorlike("composer", nil, " (muzîk)", nil, " bestekar") add_actor_role("format_as_cite") -- if the output length has changed, a credit name has been printed -- and we can print the date if output_len ~= #output then author_outputted = true add_date() end add_chapter() else add_chapter() local mainauthor = parse_and_format_multivalued_annotated_text("mainauthor") mainauthor = j_lazim(mainauthor) if mainauthor then add_with_sep(mainauthor) author_outputted = true end add_authorlike("tlr", "terciman: ", ", tercime", nil, " terciman") add_sg_and_pl_authorlike("editor", "edîtor") add_sg_and_pl_authorlike("compiler", "berhevkar") add_sg_and_pl_authorlike("director", "dîrektor") add_authorlike("lyricist", nil, " (gotin)", nil, " gotinnivîs") add_authorlike("lyrics-translator", nil, " (tercime)", nil, " tercimanê gotinan") add_authorlike("composer", nil, " (muzîk)", nil, " bestekar") end local title, title_fullname = a_with_name("title") local need_comma = false if title then local titleobj = parse_annotated_text(title, title_fullname, a("trans-title")) add(format_annotated_text(titleobj, tag_with_cite, tag_with_cite)) local series = parse_and_format_annotated_text("series") if series then add(" (" .. series) local seriesvolume = parse_and_format_annotated_text("seriesvolume") if seriesvolume then add(SEMICOLON_SPACE .. seriesvolume) end add(")") end need_comma = true elseif ind == "" then if not a("notitle") then add(maintenance_line("Jkx sernavê kitêb an kovarê binivîse")) need_comma = true end end local archiveurl, archiveurl_fullname = aurl_with_name("archiveurl") local url, url_fullname = aurl_with_name("url") local urls, urls_fullname = aurl_with_name("urls") if url and urls then error(("Supply only one of |%s= and |%s="):format(url_fullname, urls_fullname)) end local function verify_title_supplied(url_name) -- There are too many cases of this to throw an error at this time. -- if not title then -- error(("If |%s= is given, |%s= must also be supplied"):format(url_name, title_fullname)) -- end end if archiveurl or url then verify_title_supplied(archiveurl and archiveurl_fullname or url_fullname) sep = nil add("&lrm;<sup>[" .. (archiveurl or url) .. "]</sup>") elseif urls then verify_title_supplied(urls_fullname) sep = nil add("&lrm;<sup>" .. urls .. "</sup>") end -- display (in Language) if language is provided and is not English and not overriden by termlang or worklang if format_as_cite and ind == "" and not (args.termlang or args.worklang) and (args[1] or args.lang) then local lang = get_first_lang(args[1] or args.lang) if lang then local langcode = lang:getCode() if not (langcode == "und" or langcode == "ku") then local langs = format_langs(args[1] or args.lang) if langs then add(" (bi " .. langs .. ")") end end end end if need_comma then sep = ", " end local edition, edition_fullname = parse_and_format_annotated_text_with_name("edition") local edition_plain, edition_plain_fullname = parse_and_format_annotated_text_with_name("edition_plain") if edition and edition_plain then error(("Supply only one of |%s= and |%s="):format(edition_fullname, edition_plain_fullname)) end if edition then add_with_sep(edition .. " edition") end if edition_plain then add_with_sep(edition_plain) end -- Display a numeric param such as page=, volume=, column=. For each `paramname`, four params are actually -- recognized, e.g. for paramname == "page", the params page=, pages=, page_plain= and pageurl= are recognized -- and checked (or the same with an index, e.g. page2=, pages2=, page_plain2= and pageurl2= respectively if -- ind == "2"). Only one of the first three can be specified; an error results if more than one are given. -- If none are given, the return value is nil; otherwise it is a string. The numeric spec is taken directly -- from e.g. page_plain= if given; otherwise if e.g. pages= is given, or if page= is given and looks like a -- combination of numbers (i.e. it has a hyphen or dash in it, a comma, or the word " and "), it is prefixed -- by `singular_desc` + "s" (e.g. "pages "), otherwise it is prefixed by just `singular_desc` (e.g. "page "). -- (As a special case, if either e.g. page=unnumbered or pages=unnumbered is given, the numeric spec is -- "unnumbered page".) The resulting spec is returned directly unless e.g. pageurl= is given, in which case -- it is linked to the specified URL. Note that any of the specs can be foreign text, e.g. foreign numbers -- (including with optional inline modifiers), and such text is handled appropriately. local function format_numeric_param(paramname, singular_desc) local sgval = a_with_name(paramname) local sgobj = parse_annotated_text(sgval, paramname) local plparamname = paramname .. "s" local plval = a_with_name(plparamname) local plobj = parse_annotated_text(plval, plparamname) local plainval, plain_fullname = parse_and_format_annotated_text_with_name(paramname .. "_plain") local numspec if not sgval and not plval and not plainval then return elseif plainval and (sgval or plval) then error(("Can't specify " .. plain_fullname .. " with " .. paramname .. " or " .. plparamname)) elseif sgval and plval then -- if both singular and plural, display "page 1 of 1-10" numspec = singular_desc .. " " .. sgval .. " of " .. plval else -- Merge page= and pages= and treat alike because people often mix them up in both directions. if plainval then numspec = plainval else local val = sgobj and sgobj.text or plobj.text if val == "unnumbered" then numspec = "unnumbered " .. singular_desc else local desc if val:find("^!") then val = val:gsub("^!", "") desc = sgval and singular_desc or pluralize(singular_desc) else local check_val = val if check_val:find("%[") then check_val = remove_links(check_val) -- convert URL's of the form [URL DISPLAY] to the displayed value check_val = check_val:gsub("%[[^ %[%]]* ([^%[%]]*)%]", "%1") end -- in case of negative page numbers (do they exist?), don't treat as multiple pages check_val = check_val:gsub("^%-", "") -- replace HTML entity en-dashes and em-dashes with their literal codes check_val = check_val:gsub("&ndash;", "–") check_val = check_val:gsub("&#8211;", "–") check_val = check_val:gsub("&mdash;", "—") check_val = check_val:gsub("&#8212;", "—") -- Check for en-dash or em-dash, or two numbers (possibly with stuff after like 12a-15b) -- separated by a hyphen or by comma a followed by a space (to avoid firing on thousands separators). if umatch(check_val, "[–—]") or check_val:find(" and ") or check_val:match("%d+[^ ]* *%- *%d+") or check_val:match("%d+[^ ]* *, +%d+") then desc = pluralize(singular_desc) else desc = singular_desc end end local obj = sgobj or plobj obj.text = val if obj.link:find("^!") then obj.link = obj.link:gsub("^!", "") end val = format_annotated_text(obj) numspec = desc .. " " .. val end end end local url = a(paramname .. "url") if url then return "[" .. url .. " " .. numspec .. "]" else return numspec end end local volume = format_numeric_param("volume", a("volume_prefix") or "volume") if volume then add_with_sep(volume) end local issue = format_numeric_param("issue", a("issue_prefix") or "number") if issue then add_with_sep(issue) end -- number= is an alias for issue= (except in {{quote-av}}, where it is the episode number) local number = format_numeric_param("number", a("number_prefix") or "number") if number then add_with_sep(number) end local annotations = {} local genre = a("genre") if genre then insert(annotations, genre) end local format = a("format") if format then insert(annotations, format) end local medium = a("medium") if medium then insert(annotations, medium) end -- Now handle the display of language annotations like "(in French)" or -- "(quotation in Nauruan; overall work in German)". local quotelang = args[1] or args.lang if not quotelang then if ind == "" then -- This can only happen for certain non-mainspace pages, e.g. Talk pages; otherwise an error is thrown -- above. insert(annotations, maintenance_line("Please specify the language of the quote using |1=")) else -- do nothing in newversion= portion end elseif ind == "" then local worklang = a("worklang") local termlang = a("termlang") worklang = worklang or quotelang termlang = termlang or quotelang if worklang == quotelang then if worklang == termlang then -- do nothing else insert(annotations, "in " .. format_langs(quotelang)) end else if quotelang ~= termlang then insert(annotations, "quotation in " .. format_langs(quotelang)) end insert(annotations, "overall work in " .. format_langs(worklang)) end else local lang2 = a("lang2") if lang2 then insert(annotations, "in " .. format_langs(lang2)) end end if #annotations > 0 then sep = nil add_with_sep(" (" .. concat(annotations, SEMICOLON_SPACE) .. ")") end local artist = parse_and_format_multivalued_annotated_text("artist", "and") artist = j_lazim(artist) if artist then add_with_sep("performed by " .. artist) end local feat = parse_and_format_multivalued_annotated_text("feat", "and") feat = j_lazim(feat) if feat then sep = " " add_with_sep("ft. " .. feat) end if not format_as_cite then add_actor_role() end local others = parse_and_format_annotated_text("others") others = j_lazim(others) if others then add_with_sep(others) end local quoted_in = parse_and_format_annotated_text("quoted_in", tag_with_cite, tag_with_cite) if quoted_in then add_with_sep("quoted in " .. quoted_in) insert(tracking_categories, "Quotations using quoted-in parameter") end local location = parse_and_format_multivalued_annotated_text("location", "; ") local publisher = parse_and_format_multivalued_annotated_text("publisher", "; ") if publisher then if location then add_with_sep(location) -- colon sep = "&#58; " -- colon end add_with_sep(publisher) elseif location then add_with_sep(location) end if not date_outputted then add_date("no_paren") end local source = parse_and_format_multivalued_annotated_text("source", "and") if source then add_with_sep("sourced from " .. source) end local original = parse_and_format_annotated_text("original", tag_with_cite, tag_with_cite) local by = parse_and_format_multivalued_annotated_text("by", "and") by = j_lazim(by) local origtype = a("deriv") or "translation" if original or by then add_with_sep(origtype .. " of " .. (original or "original") .. (by and " by " .. by or "")) end -- Handle origlang=, origworklang=. How we handle them depends on whether the original title or author are explicitly -- given. local origlang = a("origlang") local origworklang = a("origworklang") local origlangtext, origworklangtext if origlang then origlangtext = "in " .. format_langs(origlang) end if origworklang then origworklangtext = "overall work in " .. format_langs(origworklang) end if origlang or origworklang then if original or by then local orig_annotations = {} if origlangtext then insert(orig_annotations, origlangtext) end if origworklangtext then insert(orig_annotations, origworklangtext) end sep = nil add_with_sep(" (" .. concat(orig_annotations, SEMICOLON_SPACE) .. ")") else add_with_sep(origtype .. " of original" .. (origlangtext and " " .. origlangtext or "")) if origworklangtext then sep = nil add_with_sep(" (" .. origworklangtext .. ")") end end end if ind ~= "" and has_newversion() then local formatted_new_date = format_date_args(a, get_full_paramname, alias_map, "", "", nil, "Please provide a date or year", true) if formatted_new_date then add_with_sep(formatted_new_date) end end -- Fetch date_published=/year_published=/month_published= and format appropriately. local formatted_date_published = format_date_args(a, get_full_paramname, alias_map, "", "_published", nil, nil, true) local platform = parse_and_format_multivalued_annotated_text("platform", "and") if formatted_date_published then add_with_sep("published " .. formatted_date_published .. (platform and " via " .. platform or "")) elseif platform then add_with_sep("via " .. platform) end -- From here on out, there should always be a preceding item, so we -- can dispense with add_with_sep() and always insert the comma. sep = nil local function small(txt) add(", <small>") add(txt) add("</small>") end -- Add an identifier to a book or article database such as DOI, ISBN, JSTOR, etc. `param_or_params` -- is a string identifying the base param, or a list of such strings to check in turn. If found, the value -- of the parameter is processed using `process` (a function of one argument, defaulting to mw.uri.encode()), -- and then the actual URL to insert is generated by preceding with `pretext`, following with `posttext`, -- and running the resulting string through small(), which first adds a comma and then the URL in small font. local function add_identifier(param_or_params, pretext, posttext, process) local val = a(param_or_params) if val then val = (process or mw.uri.encode)(val) small(pretext .. val .. posttext) end end add_identifier("bibcode", "[https://adsabs.harvard.edu/abs/", " →Bibcode]") add_identifier("doi", '<span class="neverexpand">[https://doi.org/', " →DOI]</span>") add_identifier("isbn", "", "", isbn) add_identifier("issn", "", "", issn) add_identifier("jstor", "[https://www.jstor.org/stable/", " →JSTOR]") add_identifier("lccn", "", "", lccn) add_identifier("oclc", "[https://search.worldcat.org/title/", " →OCLC]") add_identifier("ol", "[https://openlibrary.org/works/OL", "/ →OL]") add_identifier("pmid", "[https://www.ncbi.nlm.nih.gov/pubmed/", " →PMID]") add_identifier("pmcid", "[https://www.ncbi.nlm.nih.gov/pmc/articles/", "/ →PMCID]") add_identifier("ssrn", "[https://ssrn.com/abstract=", " →SSRN]") -- add_identifier("urn", "", "", urn) local id = a("id") if id then small(id) end archiveurl, archiveurl_fullname = aurl_with_name("archiveurl") if archiveurl then add(", archived from ") local url, url_fullname = aurl_with_name("url") if not url then -- attempt to infer original URL from archive URL; this works at -- least for Wayback Machine (web.archive.org) URL's url = archiveurl:match("/(https?:.*)$") if not url then error( ("When |%s= is specified, |%s= must also be included"):format(archiveurl_fullname, url_fullname) ) end end add("[" .. url .. " the original] on ") local archivedate, archivedate_fullname = a_with_name("archivedate") if archivedate then add(format_date(archivedate)) elseif string.sub(archiveurl, 1, 28) == "https://web.archive.org/web/" then -- If the archive is from the Wayback Machine, then it already contains the date -- Get the date and format it local wayback_date = string.sub(archiveurl, 29, 29 + 7) wayback_date = string.sub(wayback_date, 1, 4) .. "-" .. string.sub(wayback_date, 5, 6) .. "-" .. string.sub(wayback_date, 7, 8) add(format_date(wayback_date)) else error( ("When |%s= is specified, |%s= must also be included"):format( archiveurl_fullname, archivedate_fullname ) ) end end if a("accessdate") then --Otherwise do not display here, as already used as a fallback for missing date= or year= earlier. if (a("date") or a("nodate") or a("year")) and not a("archivedate") then add(", retrieved " .. format_date(a("accessdate"))) end end local formatted_section = format_chapterlike("section", "section ") if formatted_section then add(", ") add(formatted_section) end -- video game stuff local system = parse_and_format_annotated_text("system") if system then add(", " .. system) end local scene = parse_and_format_annotated_text("scene") if scene then add(", scene: " .. scene) end local level = parse_and_format_annotated_text("level") if level then add(", level/area: " .. level) end local note = parse_and_format_annotated_text("note") if note then add(", " .. note) end local note_plain = parse_and_format_annotated_text("note_plain") if note_plain then add(" " .. note_plain) end -- Wrapper around format_numeric_param that inserts the formatted text with optional preceding text. local function handle_numeric_param(paramname, singular_desc, pretext) local numspec = format_numeric_param(paramname, singular_desc) if numspec then add((pretext or "") .. numspec) end end handle_numeric_param("page", a("page_prefix") or "page", ", ") handle_numeric_param("column", a("column_prefix") or "column", ", ") handle_numeric_param("line", a("line_prefix") or "line", ", ") -- FIXME: Does this make sense? What is other=? local other = parse_and_format_annotated_text("other") if other then add(", " .. other) end end local function add_authors(args, last_first) -- Find maximum indexed author or last name. local maxind = math.max(args.author.maxindex, args.last.maxindex) -- Include max index of ancillary params so we get an error message about their use without the primary params. local ancillary_params = {"trans-author", "authorlink", "trans-authorlink", "first", "trans-first", "trans-last"} for _, ancillary in ipairs(ancillary_params) do maxind = math.max(maxind, args[ancillary].maxindex) end local num_authors = 0 for i = 1, maxind do local ind = i == 1 and "" or i local author, last = args.author[i], args.last[i] if author or last then local this_num_authors = add_author( author, "author" .. ind, args["trans-author"][i], "trans-author" .. ind, args.authorlink[i], "authorlink" .. ind, args["trans-authorlink"][i], "trans-authorlink" .. ind, args.first[i], "first" .. ind, args["trans-first"][i], "trans-first" .. ind, last, "last" .. ind, args["trans-last"][i], "trans-last" .. ind, last_first ) num_authors = num_authors + this_num_authors sep = last_first and "; " or ", " else for _, cant_have in ipairs(ancillary_params) do if args[cant_have][i] then error(("Can't have |%s%s= without |author%s= or |last%s="):format(cant_have, ind, ind, ind)) end end end end return num_authors end local function add_newversion() -- If there's a "newversion" section, add the new-version text. if has_newversion() then sep = nil --Test for new version of work. add(SEMICOLON_SPACE) if args.newversion then -- newversion= is intended for English text, e.g. "quoted in" or "republished as". add(args.newversion) elseif not args.edition2 then if has_new_title_or_author() then add("republished as") else add("republished") end end add(" ") return "" else return ", " end end ------------------- Now we start outputting text ---------------------- local need_comma = false -- Set this now so a() works just below. get_full_paramname = make_get_full_paramname("") if args.brackets then add("[") end bold_year = not format_as_cite year_last = format_as_cite formatted_date, need_date = format_date_args( a, get_full_paramname, alias_map, nil, nil, bold_year, "Can we [[:Category:Requests for date|date]] this quote?", year_last ) -- format_date_args bolds the year here (via bold_year = not format_as_cite), so this is the only date variable -- we wrap; origdate=, date_published= and the newversion date are left unwrapped. formatted_date = j_lazim(formatted_date) -- Fetch origdate=/origyear=/origmonth= and format appropriately. formatted_origdate = format_date_args(a, get_full_paramname, alias_map, "orig", nil, nil, nil, year_last) local num_authors, need_date if format_as_cite then num_authors = add_authors(args, "last_first") if author_outputted then sep = " " end -- Display all the text that comes after the author, for the main portion. postauthor("", num_authors, "format_as_cite") author_outputted = false sep = add_newversion() -- Add the newversion author(s). if args["2ndauthor"] or args["2ndlast"] then num_authors = add_author( args["2ndauthor"], "2ndauthor", nil, nil, args["2ndauthorlink"], "2ndauthorlink", nil, nil, args["2ndfirst"], "2ndfirst", nil, nil, args["2ndlast"], "2ndlast", nil, nil, "last_first" ) sep = ", " else for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do if args[cant_have] then error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have)) end end end -- Display all the text that comes after the author, for the "newversion" section. postauthor(2, num_authors, "format_as_cite") else if formatted_date then need_comma = true add(formatted_date) end if formatted_origdate then need_comma = true add(SPACE_LBRAC .. formatted_origdate .. RBRAC) end if need_comma then sep = ", " end date_outputted = true num_authors = add_authors(args) -- Display all the text that comes after the author, for the main portion. postauthor("", num_authors) author_outputted = false sep = add_newversion() -- Add the newversion author(s). if args["2ndauthor"] or args["2ndlast"] then num_authors = add_author( args["2ndauthor"], "2ndauthor", nil, nil, args["2ndauthorlink"], "2ndauthorlink", nil, nil, args["2ndfirst"], "2ndfirst", nil, nil, args["2ndlast"], "2ndlast", nil, nil ) sep = ", " else for _, cant_have in ipairs{"2ndauthorlink", "2ndfirst"} do if args[cant_have] then error(("Can't have |%s= without |2ndauthor= or |2ndlast="):format(cant_have)) end end end -- Display all the text that comes after the author, for the "newversion" section. postauthor(2, num_authors) end if not args.nocolon then sep = nil add(":") elseif args.usenodot and not args.nodot then add(".") end -- Concatenate output portions to form output text. local output_text = concat(output) -- Remainder of code handles adding categories. We add one or more of the following categories: -- -- 1. [[Category:LANG terms with quotations]], based on the first language code in termlang= or 1=. Added to -- mainspace, Reconstruction: and Appendix: pages as well as Citations: pages if the corresponding mainspace -- page exists. Not added if nocat= is given. Note that [[Module:usex]] adds the same category using the same -- logic, but we do it here too because we may not have a quotation to format. (We add in those circumstances -- because typically when there's no quotation to format, it's because it's formatted manually underneath the -- citation, or using {{ja-x}}, {{th-x}} or similar.) -- 2. [[Category:Requests for date in LANG entries]], based on the first language code in 1=. Added to mainspace, -- Reconstruction:, Appendix: and Citations: pages unless nocat= is given. -- 3. [[Category:Quotations using nocat parameter]], if nocat= is given. Added to mainspace, Reconstruction:, -- Appendix: and Citations: pages. local categories = {} local termlang = get_first_lang(args.termlang or argslang) if args.nocat then if format_as_cite then insert(tracking_categories, "Citations using nocat parameter") else insert(tracking_categories, "Quotations using nocat parameter") end else -- don't create language tracking categories for citations if not format_as_cite then local title if args.pagename then -- for testing, doc pages, etc. title = mw.title.new(args.pagename) if not title then error(("Bad value for `args.pagename`: '%s'"):format(args.pagename)) end else title = mw.title.getCurrentTitle() end -- Only add [[Citations:foo]] to [[:Category:LANG terms with quotations]] if [[foo]] exists. local ok_to_add_cat if title.nsText ~= "Citations" then ok_to_add_cat = true else local mainspace_title = mw.title.new(title.text) if mainspace_title and mainspace_title.exists then ok_to_add_cat = true end end if ok_to_add_cat then insert(categories, termlang:getFullName() .. " terms with quotations") end end if need_date then local argslangobj = get_first_lang(argslang) insert(categories, "Requests for date in " .. argslangobj:getCanonicalName() .. " entries") end end return output_text .. ( not lang and "" or (#categories > 0 and format_categories(categories, lang, args.sort) or "") .. ( #tracking_categories > 0 and format_categories( tracking_categories, lang, args.sort, nil, not page_should_be_ignored(mw.title.getCurrentTitle().fullText) ) or "" ) ) end -- Alias specs for type= and type2=. Each spec is `{canon, aliases, with_newversion}` where `canon` is the canonical -- parameter (with "2" added if type2= is being handled), `aliases` is a comma-separated string of aliases (with "2" -- added if type2= is being handled, except for numeric params), and `with_newversion` indicates whether we should -- process this spec if type2= is being handled. local type_alias_specs = { av = { {"author", "writer,writers", true}, {"chapter", "episode", true}, {"chapterurl", "episodeurl", true}, {"trans-chapter", "trans-episode", true}, {"chapter_tlr", "episode_tlr,episode_tlrs,episode_translator,episode_translators", true}, {"chapter_series", "episode_series", true}, {"chapter_seriesvolume", "episode_seriesvolume", true}, {"chapter_number", "episode_number", true}, {"chapter_plain", "episode_plain", true}, {"volume", "season", true}, {"volumes", "seasons", true}, {"volume_plain", "season_plain", true}, {"volumeurl", "seasonurl", true}, {"platform", "network", true}, }, book = { {"author", "3"}, {"editor", "amadekar"}, {"title", ",4"}, {"url", "5"}, {"year", "2"}, {"page", "6"}, {"text", "7"}, {"t", "8"}, }, journal = { {"year", "2"}, {"author", "3"}, {"chapter", "title,sernav,article,meqale,4", true}, {"chapterurl", "titleurl,articleurl,urlya-sernav,urlya-meqaleyê", true}, {"trans-chapter", "trans-title,trans-article,tercimeya-sernav,tercimeya-meqaleyê", true}, {"chapter_tlr", "title_tlr,title_tlrs,title_translator,title_translators,article_tlr,article_tlrs,article_translator,article_translators,tercimanê-sernav,tercimanên-sernav,tercimanên-meqaleyê,tercimanê-meqaleyê", true}, {"chapter_series", "title_series,article_series,seriya-sernav,seriya-meqaleyê", true}, {"chapter_seriesvolume", "title_seriesvolume,article_seriesvolume,cildê-seriya-sernav,cildê-seriya-meqaleyê", true}, {"chapter_number", "title_number,article_number,hejmara-sernav,hejmara-meqaleyê", true}, {"chapter_plain", "title_plain,article_plain,sernav-sade,meqale-sade", true}, {"title", "journal,kovar,magazine,newspaper,work,xebat,xezete,5", true}, {"trans-title", "trans-journal,tercimeya-kovar,trans-magazine,trans-newspaper,tercimeya-xezete,trans-work,tercimeya-xebat", true}, {"tlr", "journal_tlr,tercimanê-kovarê,journal_tlrs,journal_translator,journal_translators,magazine_tlr,magazine_tlrs,magazine_translator,magazine_translators,newspaper_tlr,newspaper_tlrs,newspaper_translator,tercimanê-xezeteyê,newspaper_translators,work_tlr,tercimanê-xebatê,work_tlrs,work_translator,work_translators", true}, {"url", "6,journalurl,urlya-kovarê,magazineurl,newspaperurl,urlya-xezeteyê,workurl,urlya-xebatê", true}, {"page", "7"}, {"source", "newsagency,ajans,news_agency", true}, {"text", "8"}, {"t", "9"}, }, } -- Process interally-handled aliases related to type= or type2=. `args` is a table of arguments; `typ` is the value of -- type= or type2=; newversion=true if we're dealing with type2=; alias_map is used to keep track of alias mappings -- seen. local function process_type_aliases(args, typ, newversion, alias_map) local ind = newversion and "2" or "" local deprecated = ine(args.lang) if not type_alias_specs[typ] then local possible_values = {} for possible, _ in pairs(type_alias_specs) do insert(possible_values, possible) end sort(possible_values) error( ("Unrecognized value '%s' for type%s=; possible values are %s"):format( typ, ind, concat(possible_values, ",") ) ) end for _, alias_spec in ipairs(type_alias_specs[typ]) do local canon, aliases, with_newversion = unpack(alias_spec) if with_newversion or not newversion then canon = canon .. ind aliases = split(aliases, ",", true) local saw_alias = nil for _, alias in ipairs(aliases) do if alias:match("^%d+$") then alias = tonumber(alias) if deprecated then alias = alias - 1 end else alias = alias .. ind end if args[alias] then if saw_alias == nil then saw_alias = alias else error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_alias, alias)) end end end if saw_alias and (not newversion or type(saw_alias) == "string") then if args[canon] then error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_alias, canon)) end args[canon] = args[saw_alias] -- Wipe out the original after copying. This is important in case of a param that has general significance -- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and -- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled -- parameter checking. args[saw_alias] = nil alias_map[canon] = saw_alias end end end end -- Clone and combine frame's and parent's args while also assigning nil to empty strings. Handle aliases and ignores. local function clone_args(direct_args, parent_args) local args = {} -- Processing parent args must come first so that direct args override parent args. Note that if a direct arg is -- specified but is blank, it will still override the parent arg (with nil). for pname, param in pairs(parent_args) do -- [[Special:WhatLinksHere/Wiktionary:Tracking/quote/param/PARAM]] track("param/" .. pname) args[pname] = ine(param) end -- Process ignores. The value of `ignore` is a comma-separated list of parameter names to ignore (erase). We need to -- do this before aliases due to {{quote-song}}, which sets chapter= to the value of title= in the direct params and -- sets title= to the value of album= using an alias. If we do the ignores after aliases, we get an error during alias -- processing, saying that title= and its alias album= are both present. local ignores = ine(direct_args.ignore) if ignores then for ignore in gsplit(ignores, "%s*,%s*") do args[ignore] = nil end end local alias_map = {} local other_controls = {} -- noroman local noroman = ine(direct_args.noroman) if noroman then other_controls.overrides = other_controls.overrides or {} for param in gsplit(noroman, "%s*,%s*") do other_controls.overrides[param] = (other_controls.overrides[param] or {}) other_controls.overrides[param].noroman = true end end -- Process internally-specified aliases using type= or type2=. local typ = args.type or direct_args.type or args.cure or direct_args.cure if typ then process_type_aliases(args, typ, false, alias_map) end local typ2 = args.type2 or direct_args.type2 or args.cure2 or direct_args.cure2 if typ2 then process_type_aliases(args, typ2, true, alias_map) end -- Process externally-specified aliases. The value of `alias` is a list of semicolon-separated specs, each of which -- is of the form DEST:SOURCE,SOURCE,... where DEST is the canonical name of a parameter and SOURCE refers to an -- alias. Whitespace is allowed between all delimiters. The order of aliases may be important. For example, for -- {{quote-journal}}, title= contains the article name and is an alias of underlying chapter=, while journal= or -- work= contains the journal name and is an alias of underlying title=. As a result, the title -> chapter alias -- must be specified before the journal/work -> title alias. -- -- Whenever we copy a value from argument SOURCE to argument DEST, we record an entry for the pair in alias_map, so -- that when we would display an error message about DEST, we display SOURCE instead. -- -- Do alias processing (and ignore and error_if processing) before processing direct_args so that e.g. we can set up -- an alias of title -> chapter and then set title= to something else in the direct args ({{quote-hansard}} does -- this). -- -- FIXME: Delete this once we've converted all alias processing to internal. local aliases = ine(direct_args.alias) if aliases then -- Allow and discard a trailing semicolon, to make managing multiple aliases easier. aliases = ugsub(aliases, "%s*;$", "") for alias_spec in gsplit(aliases, "%s*;%s*") do local alias_spec_parts = split(alias_spec, "%s*:%s*") if #alias_spec_parts ~= 2 then error(("Alias spec '%s' should have one colon in it"):format(alias_spec)) end local dest, sources = unpack(alias_spec_parts) sources = split(sources, "%s*,%s*") local saw_source = nil for _, source in ipairs(sources) do if source:match("^%d+$") then source = tonumber(source) end if args[source] then if saw_source == nil then saw_source = source else error(("|%s= and |%s= are aliases; cannot specify a value for both"):format(saw_source, source)) end end end if saw_source then if args[dest] then error(("|%s= is an alias of |%s=; cannot specify a value for both"):format(saw_source, dest)) end args[dest] = args[saw_source] -- Wipe out the original after copying. This important in case of a param that has general significance -- but has been redefined (e.g. {{quote-av}} redefines number= for the episode number, and -- {{quote-journal}} redefines title= for the chapter= (article). It's also important due to unhandled -- parameter checking. args[saw_source] = nil alias_map[dest] = saw_source end end end -- Process error_if. The value of `error_if` is a comma-separated list of parameter names to throw an error if seen -- in parent_args (they are params we overwrite in the direct args). local error_ifs = ine(direct_args.error_if) if error_ifs then for error_if in gsplit(error_ifs, "%s*,%s*") do if ine(parent_args[error_if]) then error( ("Cannot specify a value |%s=%s as it would be overwritten or ignored"):format( error_if, ine(parent_args[error_if]) ) ) end end end for pname, param in pairs(direct_args) do -- ignore control params if pname ~= "ignore" and pname ~= "alias" and pname ~= "error_if" and pname ~= "noroman" then args[pname] = ine(param) end end return args, alias_map, other_controls end local function get_args(frame_args, parent_args, require_lang) -- FIXME: We are processing arguments twice, once in clone_args() and then again in [[Module:parameters]]. This is -- wasteful of memory. local cloned_args, alias_map, other_controls = clone_args(frame_args, parent_args) local deprecated = ine(parent_args.lang) local alias_of_t = {alias_of = "t"} local boolean = {type = "boolean"} local language_sublist = {type = "language", sublist = true} local list_allow_holes = {list = true, allow_holes = true} local script = {type = "script"} local function alias_list_allow_holes(canon) return {alias_of = canon, list = true, allow_holes = true} end -- First, the "single" params that don't have FOO2 or FOOn versions. local params = { [deprecated and "lang" or 1] = {required = require_lang, type = "language", sublist = true, default = "und"}, ["lang2"] = language_sublist, ["ziman2"] = {alias_of = "lang2", type = "language", sublist = true}, ["newversion"] = true, ["versiyona-nû"] = {alias_of = "newversion"}, ["author"] = list_allow_holes, ["nivîskar"] = alias_list_allow_holes("author"), ["2ndauthor"] = true, ["2nivîskar"] = {alias_of = "2ndauthor"}, ["trans-author"] = list_allow_holes, ["tercimeya-nivîskar"] = alias_list_allow_holes("trans-author"), ["authorlink"] = list_allow_holes, ["lînka-nivîskar"] = alias_list_allow_holes("authorlink"), ["2ndauthorlink"] = true, ["2lînka-nivîskar"] = {alias_of = "2ndauthorlink"}, ["trans-authorlink"] = list_allow_holes, ["lînka-tercimeya-nivîskar"] = alias_list_allow_holes("trans-authorlink"), ["first"] = list_allow_holes, ["pêşnav"] = alias_list_allow_holes("first"), ["2ndfirst"] = true, ["2pêşnav"] = {alias_of = "2ndfirst"}, ["trans-first"] = list_allow_holes, ["tercimeya-pêşnav"] = alias_list_allow_holes("trans-first"), ["last"] = list_allow_holes, ["paşnav"] = alias_list_allow_holes("last"), ["2ndlast"] = true, ["2paşnav"] = {alias_of = "2ndlast"}, ["trans-last"] = list_allow_holes, ["tercimeya-paşnav"] = alias_list_allow_holes("trans-last"), ["nocat"] = boolean, ["nekat"] = {alias_of = "nocat"}, ["nocolon"] = boolean, -- quote params text = true, passage = {alias_of = "text"}, ["jêgirtin"] = {alias_of = "text"}, ["jg"] = {alias_of = "text"}, tr = true, transliteration = {alias_of = "tr"}, ts = true, transcription = {alias_of = "ts"}, norm = true, normalization = {alias_of = "norm"}, sc = script, normsc = script, sort = true, subst = true, footer = true, lit = true, ["hrf"] = {alias_of = "lit"}, t = true, translation = alias_of_t, ["tercime"] = alias_of_t, ["werger"] = alias_of_t, ["w"] = alias_of_t, gloss = alias_of_t, lb = true, ["e"] = {alias_of = "lb"}, brackets = boolean, -- original quote params origtext = true, ["metna-orîjînal"] = {alias_of = "origtext"}, origtr = true, ["tr-orîjînal"] = {alias_of = "origtr"}, origts = true, ["ts-orîjînal"] = {alias_of = "origts"}, orignorm = true, ["norm-orîjînal"] = {alias_of = "orignorm"}, origsc = script, ["sc-orîjînal"] = {alias_of = "origsc"}, orignormsc = script, ["normsc-orîjînal"] = {alias_of = "orignormsc"}, origsubst = true, ["subst-orîjînal"] = {alias_of = "origsubst"}, origlb = true, ["e-orîjînal"] = {alias_of = "origlb"}, usenodot = boolean, nodot = boolean, inline = boolean, asquote = boolean, ascite = boolean, } -- Most params have PARAM2 variants for use with `newversion`. FIXME: We should generalize this to -- allow PARAMN variants for any N. local function add_with_2(param, value) params[param] = value params[param .. "2"] = value end local function alias_with_2(alias, canon) params[alias] = {alias_of = canon} params[alias .. "2"] = {alias_of = canon .. "2"} end for _, param12 in ipairs{ "worklang", "termlang", "origlang", "origworklang" } do add_with_2(param12, language_sublist) end -- Then the newversion params (which have FOO2 versions). for _, param12 in ipairs{ -- author-like params; author params themselves are either list params (author=, last=, etc.) or single params -- (2ndauthor=, 2ndlast=, etc.) "coauthors", "quotee", "tlr", "editor", "editors", "mainauthor", "compiler", "compilers", "director", "directors", "lyricist", "lyrics-translator", "composer", "role", "actor", "artist", "feat", -- author control params "default-authorlabel", "authorlabel", -- title "title", "trans-title", "series", "seriesvolume", "notitle", -- entry "entry", "entryurl", "trans-entry", -- chapter "chapter", "chapterurl", "chapter_number", "chapter_plain", "chapter_series", "chapter_seriesvolume", "trans-chapter", "chapter_tlr", -- section "section", "sectionurl", "section_number", "section_plain", "section_series", "section_seriesvolume", "trans-section", -- other video-game params "system", "scene", "level", -- URL "url", "urls", "archiveurl", -- edition "edition", "edition_plain", -- ID params "bibcode", "doi", "isbn", "issn", "jstor", "lccn", "oclc", "ol", "pmid", "pmcid", "ssrn", "urn", "id", -- misc date params; most date params handled below "archivedate", "accessdate", "nodate", -- numeric params handled below -- other params "type", "genre", "format", "medium", "others", "quoted_in", "location", "publisher", "original", "by", "deriv", "note", "note_plain", "other", "source", "platform", } do add_with_2(param12, true) end -- Then the aliases of newversion params (which have FOO2 versions). for _, param12_aliased in ipairs{ {"role", "roles"}, {"role", "speaker"}, {"tlr", "tlrs"}, {"tlr", "translator"}, {"tlr", "translators"}, {"chapter_tlr", "chapter_tlrs"}, {"chapter_tlr", "chapter_translator"}, {"chapter_tlr", "chapter_translators"}, {"doi", "DOI"}, {"isbn", "ISBN"}, {"issn", "ISSN"}, {"jstor", "JSTOR"}, {"lccn", "LCCN"}, {"oclc", "OCLC"}, {"ol", "OL"}, {"pmid", "PMID"}, {"pmcid", "PMCID"}, {"ssrn", "SSRN"}, {"urn", "URN"}, } do local canon, alias = unpack(param12_aliased) alias_with_2(alias, canon) end -- tercimeyên Then the aliases of newversion params (which have FOO2 versions). for _, param12_aliased in ipairs{ {"role", "rol"}, {"role", "axivêr"}, {"tlr", "terciman"}, {"tlr", "wergêr"}, {"chapter_tlr", "tercimanên-beşê"}, {"chapter_tlr", "tercimanê-beşê"}, {"chapter_tlr", "tercimana-beşê"}, {"chapter_tlr", "wergêrên-beşê"}, {"chapter_tlr", "wergêrê-beşê"}, {"chapter_tlr", "wergêra-beşê"}, -- tercimeyên Then the newversion params (which have FOO2 versions). -- author-like params; author params themselves are either list params (author=, last=, etc.) or single params -- (2ndauthor=, 2ndlast=, etc.) {"coauthors", "hevnivîskar"}, {"quotee", "jê-tê-girtin"}, {"editor", "edîtor"}, {"editors", "edîtor"}, {"editor", "amadekar"}, {"mainauthor", "nivîskarê-sereke"}, {"compiler", "berhevkar"}, {"compilers", "berhevkar"}, {"director", "derhêner"}, {"directors", "derhêner"}, {"lyricist", "gotinnivîs"}, {"lyrics-translator", "tercimanê-gotinan"}, {"lyrics-translator", "tercimana-gotinan"}, {"lyrics-translator", "wergêrê-gotinan"}, {"lyrics-translator", "wergêra-gotinan"}, {"composer", "bestekar"}, {"actor", "aktor"}, {"actor", "lîstikvan"}, {"artist", "hunermend"}, {"artist", "artîst"}, {"feat", "mêvan"}, --tercimeyên author control params {"default-authorlabel", "standard-etîketa-nivîskar"}, {"authorlabel", "etîketa-nivîskar"}, --tercimeyên title {"title", "sernav"}, {"trans-title", "tercimeya-sernav"}, {"trans-title", "wergera-sernav"}, {"series", "serî"}, {"series", "rêze"}, {"seriesvolume", "cildê-seriyê"}, {"seriesvolume", "cildê-rêzeyê"}, {"notitle", "bêsernav"}, --tercimeyên entry {"entry", "made"}, {"entryurl", "urlya-madeyê"}, {"trans-entry", "tercimeya-madeyê"}, {"trans-entry", "wergera-madeyê"}, --tercimeyên chapter {"chapter", "beş"}, {"chapterurl", "urlya-beşê"}, {"chapter_number", "hejmara-beşê"}, {"chapter_plain", "beş-sade"}, {"chapter_series", "seriya-beşê"}, {"chapter_series", "rêzeya-beşê"}, {"chapter_seriesvolume", "cildê-seriya-beşê"}, {"chapter_seriesvolume", "cildê-rêzeya-beşê"}, {"trans-chapter", "tercimeya-beşê"}, {"trans-chapter", "wergera-beşê"}, --tercimeyên section {"section", "qisim"}, {"section", "qism"}, {"sectionurl", "urlya-qismê"}, {"section_number", "hejmara-qismê"}, {"section_plain", "qisim-sade"}, {"section_plain", "qism-sade"}, {"section_series", "rêzeya-qismê"}, {"section_seriesvolume", "cildê-seriya-qismê"}, {"section_seriesvolume", "cildê-rêzeya-qismê"}, {"trans-section", "tercimeya-qismê"}, --tercimeyên other video-game params {"system", "sîstem"}, {"system", "pergal"}, {"scene", "sehne"}, --{"level", "level"}, --tercimeyên URL {"archiveurl", "urlya-arşîvê"}, -- tercimeyên edition {"edition", "çap"}, {"edition_plain", "çap-sade"}, --tercimeyên misc date params; most date params handled below {"archivedate", "roja-arşîvê"}, {"archivedate", "tarîxa-arşîvê"}, {"accessdate", "tarîxa-gihiştinê"}, {"accessdate", "roja-gihiştinê"}, {"nodate", "bêtarîx"}, --tercimeyên other params {"type", "cure"}, {"genre", "janr"}, {"medium", "medya"}, {"others", "kesên-din"}, {"others", "yên-din"}, {"quoted_in", "cihê-jêgirtinê"}, {"location", "cih"}, {"location", "cîh"}, {"publisher", "weşanxane"}, {"publisher", "weşanger"}, {"original", "orîjînal"}, {"original", "orjînal"}, {"by", "ji-aliyê"}, {"by", "ji-hêla"}, --{"deriv", ""}, {"note", "not"}, {"note_plain", "not-sade"}, {"other", "yê-din"}, {"source", "çavkanî"}, --tercimeyên {"worklang", "zimanê-eserê"}, {"termlang", "zimanê-termê"}, {"origlang", "zimanê-orîjînal"}, {"origworklang", "zimanê-esera-orîjînal"}, } do local canon, alias = unpack(param12_aliased) alias_with_2(alias, canon) end -- Then the date params. for _, datelike in ipairs{{"", ""}, {"orig", ""}, {"", "_published"}} do local pref, suf = unpack(datelike) for _, arg in ipairs{"date", "year", "month", "start_date", "start_year", "start_month"} do add_with_2(pref .. arg .. suf, true) end end -- Kurmancî aliases for the date params for _, date_aliased in ipairs{ {"date", "tarîx"}, {"date", "dîrok"}, {"year", "sal"}, {"month", "meh"}, {"start_date", "tarîxa-destpêkê"}, {"start_year", "sala-destpêkê"}, {"start_month", "meha-destpêkê"}, {"origdate", "tarîxa-orîjînal"}, {"origyear", "sala-orîjînal"}, {"origyear", "sala orîjînal"}, {"origmonth", "meha-orîjînal"}, {"origstart_date", "tarîxa-destpêkê-orîjînal"}, {"origstart_year", "sala-destpêkê-orîjînal"}, {"origstart_month", "meha-destpêkê-orîjînal"}, {"date_published", "tarîxa-weşandinê"}, {"year_published", "sala-weşandinê"}, {"year_published", "sala weşandinê"}, {"month_published", "meha-weşandinê"}, {"start_date_published", "tarîxa-destpêkê-weşandin"}, {"start_year_published", "sala_destpêkê-weşandin"}, {"start_month_published", "meha-destpêkê-weşandin"} } do local canon, alias = unpack(date_aliased) alias_with_2(alias, canon) end local numeric_param_suffixes = {"", "s", "_plain", "url", "_prefix"} -- Then the numeric params. for _, numeric in ipairs{"volume", "issue", "number", "line", "page", "column"} do for _, suf in ipairs(numeric_param_suffixes) do add_with_2(numeric .. suf, true) end end -- canon word -> Kurdish forms -- aliases : plain synonyms (maps straight to canon, e.g. "volume") -- sade : ezafe form used before "-sade" (canon .. "_plain") -- urlya : ezafe form used after "urlya-" (canon .. "url") local ku_numeric = { volume = {aliases = {"cild", "cîld"}, sade = "cild", urlya = "cild"}, issue = {aliases = {"hj", "hejmar"}, sade = "hejmar", urlya = "hejmarê"}, number = {aliases = {"hj", "hejmar"}, sade = "hejmar", urlya = "hejmarê"}, line = {aliases = {"rêz"}, sade = "rêz", urlya = "rêzê"}, page = {aliases = {"rûpel", "r"}, sade = "rûpel", urlya = "rûpelê"}, column = {aliases = {"stûn"}, sade = "stûn", urlya = "stûnê"}, } for canon, t in pairs(ku_numeric) do for _, alias in ipairs(t.aliases) do alias_with_2(alias, canon) end alias_with_2(t.sade .. "-sade", canon .. "_plain") alias_with_2("urlya-" .. t.urlya, canon .. "url") alias_with_2("pêşgira-" .. t.urlya, canon .. "_prefix") end -- And the aliases of numeric params. for _, numeric_aliased in ipairs{{"volume", "vol"}} do local canon, alias = unpack(numeric_aliased) for _, suf in ipairs(numeric_param_suffixes) do alias_with_2(alias .. suf, canon .. suf) end end return process_params(cloned_args, params), alias_map, other_controls end local function get_origtext_params(args) local origtext, origtextlang, origsc, orignormsc if args.origtext then -- Wiktionary language codes have at least two lowercase letters followed possibly by lowercase letters and/or -- hyphens (there are more restrictions but this is close enough). Also check for nonstandard Latin etymology -- language codes (e.g. VL. or LL.). (There used to be more nonstandard codes but they have all been -- eliminated.) origtextlang, origtext = args.origtext:match("^(%l%l[%l-]*):([^ ].*)$") if not origtextlang then -- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL. origtextlang, origtext = args.origtext:match("^(%uL%.):([^ ].*)$") end if not origtextlang then error("origtext= should begin with a language code prefix") end origtextlang = get_lang(origtextlang, nil, "allow etym") or error("origtext= should begin with a language code prefix") origsc = args.origsc orignormsc = args.orignormsc else for _, noparam in ipairs{"origtr", "origts", "origsc", "orignorm", "orignormsc", "origsubst", "origlb"} do if args[noparam] then error(("Cannot specify %s= without origtext="):format(noparam)) end end end return origtext, origtextlang, origsc, orignormsc end local function get_quote(args, is_cite, source) local text = args.text local gloss = args.t local tr = args.tr local ts = args.ts local norm = args.norm local sc = args.sc local normsc = args.normsc -- Fetch original-text parameters. local origtext, origtextlang, origsc, orignormsc = get_origtext_params(args) -- If any quote-related args are present, display the actual quote; otherwise, display nothing. if text or gloss or tr or ts or norm or args.origtext then -- Pass "und" here rather than cause an error; there will be an error on mainspace, Citations, etc. pages -- in any case in source() if the language is omitted. local lang = get_first_lang(args[1] or args.lang) local termlang = args.termlang and get_first_lang(args.termlang) or lang local usex_data = { lang = lang, termlang = termlang, usex = text, sc = sc, translation = gloss, normalization = norm, normsc = normsc, transliteration = tr, transcription = ts, brackets = args.brackets, subst = args.subst, lit = args.lit, footer = args.footer, qq = parse_and_format_labels(args.lb, lang), quote = "quote-meta", orig = origtext, origlang = origtextlang, origsc = origsc, orignorm = args.orignorm, orignormsc = orignormsc, origtr = args.origtr, origts = args.origts, origsubst = args.origsubst, origqq = parse_and_format_labels(args.origlb, lang), noreq = args.noreq, nocat = is_cite or args.nocat, source = source } if args.inline then -- don't let usex format the footer, otherwise it gets inlined with the rest of the quoted text usex_data.footer = nil usex_data.inline = 1 text = format_usex(usex_data) if text then text = " “" .. text .. "”" else text = "" end if args.footer then text = text .. "<dl><dd>" .. args.footer .. "</dd></dl>" end else text = format_usex(usex_data) end elseif args.footer then text = "<dl><dd>" .. args.footer .. "</dd></dl>" end return text end -- External interface, meant to be called from a template. Replaces {{quote-meta}} and meant to be the primary -- interface for {{quote-*}} templates. function export.quote_t(frame) local args, alias_map, other_controls = get_args(frame.args, frame:getParent().args, "require_lang") if args.ascite then return export.cite_t(frame) end return show_quote(args, alias_map, other_controls) end function show_quote(args, alias_map, other_controls) local parts = {} insert(parts, get_quote(args, false, export.source(args, alias_map, nil, other_controls))) local retval = concat(parts) return deprecated and frame:expandTemplate({ title = "check deprecated lang param usage", args = {retval, lang = args.lang}, }) or retval end -- External interface, meant to be called from a template. Replaces {{cite-meta}} and meant to be the primary -- interface for {{cite-*}} templates. function export.cite_t(frame) local parent_args = {} for k, v in pairs(frame:getParent().args) do parent_args[k] = v end -- use "und" as lang if none provided if parent_args[1] == nil then parent_args[1] = "und" end local args, alias_map, other_controls = get_args(frame.args, parent_args) if args.asquote then return show_quote(args, alias_map, other_controls) end return show_cite(args, alias_map, other_controls) end function show_cite(args, alias_map, other_controls) local parts = {} -- don't nag for translations if args.text and not args.t then args.noreq = 1 end local len_visible = args.text and ulen((args.text:gsub("<[^<>]+>", ""))) or 0 if len_visible == 0 then if not args.t or args.t == "-" then args.nocolon = true end elseif args.inline == nil then args.inline = not ( args.block_text or len_visible > 300 or (args.t and not args.t == "-" and len_visible < 80) or string.match(args.text, "<br>") ) end insert(parts, '<span class="citation-whole"><span class="cited-source">') insert(parts, export.source(args, alias_map, "format_as_cite", other_controls)) insert(parts, "</span>") insert(parts, get_quote(args, "is_cite")) insert(parts, "</span>") local retval = concat(parts) return deprecated and frame:expandTemplate({ title = "check deprecated lang param usage", args = {retval, lang = args.lang}, }) or retval end -- External interface, meant to be called from a template. function export.call_quote_template(frame) return export.call_template(frame) end -- External interface, for calling a template with overloaded parameters. function export.overload(frame) local iargs, overloaded_args = process_params(frame.args, { [1] = {required = true} }, true) local parent_args = frame:getParent().args for k, v in pairs(parent_args) do overloaded_args[k] = v end return frame:expandTemplate{title = iargs[1], args = overloaded_args} end -- External interface, meant to be called from a template. function export.call_template(frame) local parameter_sublist = {type = "parameter", sublist = true} local iargs, other_direct_args = process_params(frame.args, { ["template"] = true, ["textparam"] = parameter_sublist, ["pageparam"] = parameter_sublist, ["propagateparams"] = parameter_sublist, ["allowparams"] = {sublist = true}, -- Doesn't use type = "parameter", because any that end in :list get processed differently. }, true) local function fetch_param(source, params) for _, param in ipairs(params) do if source[param] then return source[param] end end return nil end local params = { ["text"] = true, ["passage"] = true, ["footer"] = true, -- always propagates, but gets special formatting } -- paramaters that can be used during template declaration or included in template calls -- if the template is called with param=- it will set any declared value to nil local always_propagate = { ["brackets"] = true, ["usenodot"] = true, ["nodot"] = true, ["ascite"] = true, ["asquote"] = true, } for k,v in pairs(always_propagate) do params[k] = v end local textparam = iargs.textparam or {} for _, param in ipairs(textparam) do params[param] = true end local pageparam = iargs.pageparam or {} local pageparam1 = pageparam[1] if pageparam1 ~= nil then params["page"], params["pages"] = true, true for _, param in ipairs(pageparam) do params[param] = true end end local allowparams, allow_all, list = iargs.allowparams, false if allowparams ~= nil then for _, allow in ipairs(allowparams) do local param = allow:match("^(.*):list$") if param then if list == nil then list = {list = true} end params[scribunto_parameter_key(param)] = list elseif allow == "*" then track("no parameter checking") allow_all = true else params[scribunto_parameter_key(allow)] = true end end else local parent = frame:getParent() local template_name = parent:getTitle() local template_title = new_title(template_name) local content = template_title:getContent() local allowed_params, seen = {}, {} -- Detect all params used by the parent template. param:get_name() takes the -- parent frame arg table as an argument so that preprocessing will take -- them into account, since it will matter if the name contains another -- parameter (e.g. the outer param in "{{{foo{{{bar}}}baz}}}" will change -- depending on the value for bar=). `seen` memoizes results based on the -- raw parameter text (which is stored as a string in the parameter object), -- which avoids unnecessary param:get_name() calls, which are non-trivial. for param in find_parameters(content) do local raw = param.raw if not seen[raw] then allow = param:get_name(template_args) params[scribunto_parameter_key(allow)] = true seen[raw] = true end end end local propagateparams = iargs.propagateparams or {} for _, param in ipairs(propagateparams) do params[param] = true end local parent_args = frame:getParent().args local args = process_params(parent_args, params, allow_all) parent_args = shallow_copy(parent_args) if textparam[1] ~= "-" then other_direct_args.passage = args.text or args.passage or fetch_param(args, textparam) end if not (pageparam1 == nil or pageparam1 == "-") then other_direct_args.page = fetch_param(args, pageparam) or args.page or other_direct_args.page or nil other_direct_args.pages = args.pages or other_direct_args.pages or nil if other_direct_args.page == "-" then other_direct_args.page = nil end if other_direct_args.pages == "-" then other_direct_args.pages = nil end end if args.footer then other_direct_args.footer = frame:expandTemplate{title = "small", args = {args.footer}} end for param, _ in pairs(always_propagate) do if args[param] == "-" then other_direct_args[param] = nil else other_direct_args[param] = args[param] or other_direct_args[param] or nil end end -- authorlink=- can be used to prevent copying of author= to authorlink= but we don't want to propagate this to -- the actual {{quote-*}} code. if other_direct_args.authorlink == "-" then other_direct_args.authorlink = nil end for _, param in ipairs(propagateparams) do if args[param] then other_direct_args[param] = args[param] end end return frame:expandTemplate{title = iargs.template or "quote-book", args = other_direct_args} end local paramdoc_param_replacements = { passage = { param_with_synonym = "<<synonym>>, {{para|text}}, or {{para|passage}}", param_no_synonym = "{{para|text}} or {{para|passage}}", text = [=[ * <<params>> – the passage to be quoted.]=], }, page = { param_with_synonym = "<<synonym>> or {{para|page}}, or {{para|pages}}", param_no_synonym = "{{para|page}} or {{para|pages}}", text = [=[ * <<params>> – '''mandatory in some cases''': the page number(s) quoted from. When quoting a range of pages, note the following: ** Separate the first and last pages of the range with an [[en dash]], like this: {{para|pages|10–11}}. ** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears). : This parameter must be specified to have the template link to the online version of the work.]=], }, page_with_roman_preface = { param_with_synonym = {"inherit", "page"}, param_no_synonym = {"inherit", "page"}, text = [=[ * <<params>> – '''mandatory in some cases''': the page number(s) quoted from. If quoting from the preface, specify the page number(s) in lowercase Roman numerals. When quoting a range of pages, note the following: ** Separate the first and last page number of the range with an [[en dash]], like this: {{para|pages|10–11}} or {{para|pages|iii–iv}}. ** You must also use {{para|pageref}} to indicate the page to be linked to (usually the page on which the Wiktionary entry appears). : This parameter must be specified to have the template link to the online version of the work.]=], }, chapter = { param_with_synonym = "<<synonym>> or {{para|chapter}}", param_no_synonym = "{{para|chapter}}", text = [=[ * <<params>> – the name of the chapter quoted from.]=], }, roman_chapter = { param_with_synonym = {"inherit", "chapter"}, param_no_synonym = {"inherit", "chapter"}, text = [=[ * <<params>> – the chapter number quoted from in uppercase Roman numerals.]=], }, arabic_chapter = { param_with_synonym = {"inherit", "chapter"}, param_no_synonym = {"inherit", "chapter"}, text = [=[ * <<params>> – the chapter number quoted from in Arabic numerals.]=], }, trailing_params = { text = [=[ * {{para|footer}} – a comment on the passage quoted. * {{para|brackets}} – use {{para|brackets|on}} to surround a quotation with [[bracket#Noun|brackets]]. This indicates that the quotation either contains a mere mention of a term (for example, “some people find the word '''''manoeuvre''''' hard to spell”) rather than an actual use of it (for example, “we need to '''manoeuvre''' carefully to avoid causing upset”), or does not provide an actual instance of a term but provides information about related terms.]=], }, } function export.paramdoc(frame) local parargs = frame:getParent().args local args = process_params(parargs, { [1] = true, }) local text = args[1] local function do_param_with_optional_synonym(param, text_to_sub, paramtext_synonym, paramtext_no_synonym) local function sub_param(synonym) local subbed_paramtext if synonym then subbed_paramtext = paramtext_synonym:gsub("<<synonym>>", "{{para|" .. replacement_escape(synonym) .. "}}") else subbed_paramtext = paramtext_no_synonym end return frame:preprocess((text_to_sub:gsub("<<params>>", replacement_escape(subbed_paramtext)))) end text = text:gsub("<<" .. pattern_escape(param) .. ">>", function() return sub_param() end) text = text:gsub("<<" .. pattern_escape(param) .. ":(.-)>>", sub_param) end local function fetch_text(param_to_replace, key) local spec = paramdoc_param_replacements[param_to_replace] local val = spec[key] if type(val) == "string" then return val end if type(val) == "table" and val[1] == "inherit" then return fetch_text(val[2], key) end error( "Internal error: Unrecognized value for param '" .. param_to_replace .. "', key '" .. key .. "': " .. mw.dumpObject(val) ) end for param_to_replace, spec in pairs(paramdoc_param_replacements) do if not spec.param_no_synonym then -- Text to substitute directly. text = text:gsub("<<" .. pattern_escape(param_to_replace) .. ">>", function() return frame:preprocess(fetch_text(param_to_replace, "text")) end) else do_param_with_optional_synonym( param_to_replace, fetch_text(param_to_replace, "text"), fetch_text(param_to_replace, "param_with_synonym"), fetch_text(param_to_replace, "param_no_synonym") ) end end -- Remove final newline so template code can add a newline after invocation text = text:gsub("\n$", "") return text end return export 72arc6j2gne95e89f35bo3s7eswd1vq MediaWiki:Gadget-start.js/data.json 8 1357737 6291579 6276921 2026-07-26T17:21:16Z Wikihez 7344 6291579 json application/json { "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ê û nêr", "mnmane": "mê yan nêr li gorî maneyê", "g": "giştî", "y": "yekjimar", "p": "pirjimar" }, "tplDefs": [ { "name": "awayê Jacobsonê", "langs": [ "zza" ] }, { "name": "rastnivîs", "langs": [ "ku" ], "noLang": true }, { "name": "ku-Latn", "langs": [ "ku" ], "noLang": true }, { "name": "sdh-Arab", "langs": [ "sdh" ], "noLang": true }, { "name": "hac-Arab", "langs": [ "hac" ], "noLang": true }, { "name": "ja-romanîzekirin", "langs": [ "ja" ], "noLang": true }, { "name": "ckb-Arab", "langs": [ "ckb" ], "noLang": true }, { "name": "awayekî din", "langs": null }, { "name": "guharto", "langs": null }, { "name": "hevmaneya", "langs": null }, { "name": "kevnbûyî", "langs": null }, { "name": "kêmpeyda", "langs": null }, { "name": "şaşnivîs", "langs": null } ], "jederDefs": { "jêder-kitêb": { "label": "Kitêb", "fields": [ { "key": "nivîskar", "label": "Nivîskar" }, { "key": "sernav", "label": "Sernav" }, { "key": "sernavê orjînal", "label": "Sernavê orjînal" }, { "key": "terciman", "label": "Terciman" }, { "key": "weşanxane", "label": "Weşanxane" }, { "key": "cih", "label": "Cih" }, { "key": "sal", "label": "Sal" }, { "key": "çap", "label": "Çap" }, { "key": "rûpel", "label": "Rûpel" }, { "key": "isbn", "label": "ISBN" }, { "key": "url", "label": "URL" } ] }, "jêder-kovar": { "label": "Kovar", "fields": [ { "key": "nivîskar", "label": "Nivîskar" }, { "key": "nivîskar2", "label": "Nivîskar 2" }, { "key": "sernav", "label": "Sernav" }, { "key": "sernavê orjînal", "label": "Sernavê orjînal" }, { "key": "kovar", "label": "Kovar" }, { "key": "terciman", "label": "Terciman" }, { "key": "weşanxane", "label": "Weşanxane" }, { "key": "cild", "label": "Cild" }, { "key": "hejmar", "label": "Hejmar" }, { "key": "sal", "label": "Sal" }, { "key": "rûpel", "label": "Rûpel" }, { "key": "url", "label": "URL" } ] }, "jêder-websîte": { "label": "Websîte", "fields": [ { "key": "nivîskar", "label": "Nivîskar" }, { "key": "sernav", "label": "Sernav" }, { "key": "terciman", "label": "Terciman" }, { "key": "websîte", "label": "Websîte" }, { "key": "url", "label": "URL" }, { "key": "arşîv", "label": "Arşîva URLê" }, { "key": "sal", "label": "Sal" }, { "key": "tarîx", "label": "Tarîx" } ] } }, "pronTpls": [ { "name": "ku-IPA", "desc": "IPAya otomatîk ji bo kurmancî (kontrol hewce ye)", "snippet": "* {{ku-IPA}}", "langs": [ "ku" ], "noLang": true }, { "name": "ku-kîte", "desc": "Hecekirina otomatîk ji bo kurmancî", "snippet": "* {{ku-kîte}}", "langs": [ "ku" ], "noLang": true }, { "name": "zza-IPA", "desc": "IPAya otomatîk ji bo zazakî", "snippet": "* {{zza-IPA}}", "langs": [ "zza" ], "noLang": true }, { "name": "zza-kîte", "desc": "Hecekirina otomatîk ji bo zazakî", "snippet": "* {{zza-kîte}}", "langs": [ "zza" ], "noLang": true }, { "name": "IPA", "desc": "Alfabeya fonetîk a navmiletî (IPA)", "snippet": "* {{IPA|CODE|/…/}}" }, { "name": "bilêv", "desc": "{{bilêv|herf|nerm, req|hejmar}}", "snippet": "* {{bilêv|herf|cure|hejmar}}" }, { "name": "kîte", "desc": "Şablona hecekirinê", "snippet": "* {{kîte|CODE|he|ce}}" }, { "name": "hevdeng", "desc": "Hevdeng — gotinên bi heman dengî", "snippet": "* {{hevdeng|CODE|…}}" } ], "etymTpls": [ { "name": "ji", "desc": "Ji zimanekî din hatiye — {{ji|en|ku|anatomy|tr=|t=}}", "snippet": "{{ji|SOURCELANG|CODE|…}}" }, { "name": "deyn", "desc": "Deynkirî — {{deyn|lang|kod|kelîme}}", "snippet": "{{deyn|SOURCELANG|CODE|…}}" }, { "name": "mîr", "desc": "Mîrasmayî — {{mîr|lang|kod|kelîme}}", "snippet": "{{mîr|SOURCELANG|CODE|…}}" }, { "name": "qertaf", "desc": "Ji bi pêşgir, paşgir û hevedudanî — {{qertaf|kod|pêşgir-|reh}}", "snippet": "{{qertaf|CODE|…|…}}" }, { "name": "q", "desc": "Referans bo rûpelekê — {{q|kod|kelîme}} navê zimanî dernakeve", "snippet": "{{q|CODE|…}}" }, { "name": "hv", "desc": "Hevreh — {{hv|lang|kelîme}} navê zimanî derdikeve", "snippet": "{{hv|SOURCELANG|…}}" }, { "name": "nh", "desc": "Nehevreh — {{nh|lang|kelîme}} navê zimanî derdikeve", "snippet": "{{nh|SOURCELANG|…}}" }, { "name": "cotreh", "desc": "Cotreh — teror {{cotreh|ku|tirs}}", "snippet": "{{cotreh|CODE|…}}" }, { "name": "yekser", "desc": "tercimeya yekser — {{yekser|lang|kod|kelîme}}", "snippet": "{{yekser|SOURCELANG|CODE|…}}" }, { "name": "wergirtin", "desc": "Wergirtina maneyê — {{wergirtin|lang|kod|kelîme}}", "snippet": "{{wergirtin|SOURCELANG|CODE|…}}" }, { "name": "paşvesazî", "desc": "Paşvesazî — greed {{paşvesazî|en|greedy}}", "snippet": "{{paşvesazî|CODE|…}}" }, { "name": "qusandin", "desc": "Qusandin — Miho {{qusandin|ku|Mihemmed}}", "snippet": "{{qusandin|CODE|…}}" }, { "name": "têkilkirin", "desc": "Têkilkirin — sormancî {{têkilkirin|ku|soranî|kurmancî}}", "snippet": "{{têkilkirin|CODE|…}}" }, { "name": "nyz", "desc": "Nayê zanîn — {{nyz|ku|hûr=1}}", "snippet": "{{nyz|CODE}}" }, { "name": "onom", "desc": "Onomatopoetîk — {{onom|ku|hûr=1}}", "snippet": "{{onom|CODE}}" }, { "name": "navmiletî", "desc": "Navmiletî — {{navmiletî|ku|hûr=1}}", "snippet": "{{navmiletî|CODE}}" }, { "name": "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", "mn", "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" } } } ikqomxr80fj3nzidvn0fgxscm4cpbl7 6291583 6291579 2026-07-26T17:31:10Z Wikihez 7344 Restored revision 6276921 by [[Special:Contributions/Wikihez|Wikihez]] ([[User talk:Wikihez|talk]]) ([[:m:User:Xiplus/TwinkleGlobal|TwinkleGlobal]]) 6291583 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ê û nêr", "mnmane": "mê yan nêr li gorî maneyê", "g": "giştî", "y": "yekjimar", "p": "pirjimar" }, "tplDefs": [ { "name": "awayê Jacobsonê", "langs": [ "zza" ] }, { "name": "rastnivîs", "langs": [ "ku" ], "noLang": true }, { "name": "ku-Latn", "langs": [ "ku" ], "noLang": true }, { "name": "sdh-Arab", "langs": [ "sdh" ], "noLang": true }, { "name": "hac-Arab", "langs": [ "hac" ], "noLang": true }, { "name": "ja-romanîzekirin", "langs": [ "ja" ], "noLang": true }, { "name": "ckb-Arab", "langs": [ "ckb" ], "noLang": true }, { "name": "awayekî din", "langs": null }, { "name": "guharto", "langs": null }, { "name": "hevmaneya", "langs": null }, { "name": "kevnbûyî", "langs": null }, { "name": "kêmpeyda", "langs": null }, { "name": "şaşnivîs", "langs": null } ], "jederDefs": { "jêder-kitêb": { "label": "Kitêb", "fields": [ { "key": "nivîskar", "label": "Nivîskar" }, { "key": "sernav", "label": "Sernav" }, { "key": "sernavê orjînal", "label": "Sernavê orjînal" }, { "key": "terciman", "label": "Terciman" }, { "key": "weşanxane", "label": "Weşanxane" }, { "key": "cih", "label": "Cih" }, { "key": "sal", "label": "Sal" }, { "key": "çap", "label": "Çap" }, { "key": "rûpel", "label": "Rûpel" }, { "key": "isbn", "label": "ISBN" }, { "key": "url", "label": "URL" } ] }, "jêder-kovar": { "label": "Kovar", "fields": [ { "key": "nivîskar", "label": "Nivîskar" }, { "key": "nivîskar2", "label": "Nivîskar 2" }, { "key": "sernav", "label": "Sernav" }, { "key": "sernavê orjînal", "label": "Sernavê orjînal" }, { "key": "kovar", "label": "Kovar" }, { "key": "terciman", "label": "Terciman" }, { "key": "weşanxane", "label": "Weşanxane" }, { "key": "cild", "label": "Cild" }, { "key": "hejmar", "label": "Hejmar" }, { "key": "sal", "label": "Sal" }, { "key": "rûpel", "label": "Rûpel" }, { "key": "url", "label": "URL" } ] }, "jêder-websîte": { "label": "Websîte", "fields": [ { "key": "nivîskar", "label": "Nivîskar" }, { "key": "sernav", "label": "Sernav" }, { "key": "terciman", "label": "Terciman" }, { "key": "websîte", "label": "Websîte" }, { "key": "url", "label": "URL" }, { "key": "arşîv", "label": "Arşîva URLê" }, { "key": "sal", "label": "Sal" }, { "key": "tarîx", "label": "Tarîx" } ] } }, "pronTpls": [ { "name": "ku-IPA", "desc": "IPAya otomatîk ji bo kurmancî (kontrol hewce ye)", "snippet": "* {{ku-IPA}}", "langs": [ "ku" ], "noLang": true }, { "name": "ku-kîte", "desc": "Hecekirina otomatîk ji bo kurmancî", "snippet": "* {{ku-kîte}}", "langs": [ "ku" ], "noLang": true }, { "name": "zza-IPA", "desc": "IPAya otomatîk ji bo zazakî", "snippet": "* {{zza-IPA}}", "langs": [ "zza" ], "noLang": true }, { "name": "zza-kîte", "desc": "Hecekirina otomatîk ji bo zazakî", "snippet": "* {{zza-kîte}}", "langs": [ "zza" ], "noLang": true }, { "name": "IPA", "desc": "Alfabeya fonetîk a navmiletî (IPA)", "snippet": "* {{IPA|CODE|/…/}}" }, { "name": "bilêv", "desc": "{{bilêv|herf|nerm, req|hejmar}}", "snippet": "* {{bilêv|herf|cure|hejmar}}" }, { "name": "kîte", "desc": "Şablona hecekirinê", "snippet": "* {{kîte|CODE|he|ce}}" }, { "name": "hevdeng", "desc": "Hevdeng — gotinên bi heman dengî", "snippet": "* {{hevdeng|CODE|…}}" } ], "etymTpls": [ { "name": "ji", "desc": "Ji zimanekî din hatiye — {{ji|en|ku|anatomy|tr=|t=}}", "snippet": "{{ji|SOURCELANG|CODE|…}}" }, { "name": "deyn", "desc": "Deynkirî — {{deyn|lang|kod|kelîme}}", "snippet": "{{deyn|SOURCELANG|CODE|…}}" }, { "name": "mîr", "desc": "Mîrasmayî — {{mîr|lang|kod|kelîme}}", "snippet": "{{mîr|SOURCELANG|CODE|…}}" }, { "name": "qertaf", "desc": "Ji bi pêşgir, paşgir û hevedudanî — {{qertaf|kod|pêşgir-|reh}}", "snippet": "{{qertaf|CODE|…|…}}" }, { "name": "q", "desc": "Referans bo rûpelekê — {{q|kod|kelîme}} navê zimanî dernakeve", "snippet": "{{q|CODE|…}}" }, { "name": "hv", "desc": "Hevreh — {{hv|lang|kelîme}} navê zimanî derdikeve", "snippet": "{{hv|SOURCELANG|…}}" }, { "name": "nh", "desc": "Nehevreh — {{nh|lang|kelîme}} navê zimanî derdikeve", "snippet": "{{nh|SOURCELANG|…}}" }, { "name": "cotreh", "desc": "Cotreh — teror {{cotreh|ku|tirs}}", "snippet": "{{cotreh|CODE|…}}" }, { "name": "yekser", "desc": "tercimeya yekser — {{yekser|lang|kod|kelîme}}", "snippet": "{{yekser|SOURCELANG|CODE|…}}" }, { "name": "wergirtin", "desc": "Wergirtina maneyê — {{wergirtin|lang|kod|kelîme}}", "snippet": "{{wergirtin|SOURCELANG|CODE|…}}" }, { "name": "paşvesazî", "desc": "Paşvesazî — greed {{paşvesazî|en|greedy}}", "snippet": "{{paşvesazî|CODE|…}}" }, { "name": "qusandin", "desc": "Qusandin — Miho {{qusandin|ku|Mihemmed}}", "snippet": "{{qusandin|CODE|…}}" }, { "name": "têkilkirin", "desc": "Têkilkirin — sormancî {{têkilkirin|ku|soranî|kurmancî}}", "snippet": "{{têkilkirin|CODE|…}}" }, { "name": "nyz", "desc": "Nayê zanîn — {{nyz|ku|hûr=1}}", "snippet": "{{nyz|CODE}}" }, { "name": "onom", "desc": "Onomatopoetîk — {{onom|ku|hûr=1}}", "snippet": "{{onom|CODE}}" }, { "name": "navmiletî", "desc": "Navmiletî — {{navmiletî|ku|hûr=1}}", "snippet": "{{navmiletî|CODE}}" }, { "name": "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", "mn", "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" } } } 26wzz3z4txlu6946jit59b7kmjiyso5 6291584 6291583 2026-07-26T17:35:10Z Wikihez 7344 6291584 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ê û nêr", "mnmane": "mê yan nêr li gorî maneyê", "g": "giştî", "y": "yekjimar", "p": "pirjimar" }, "tplDefs": [ { "name": "awayê Jacobsonê", "langs": [ "zza" ] }, { "name": "rastnivîs", "langs": [ "ku" ], "noLang": true }, { "name": "ku-Latn", "langs": [ "ku" ], "noLang": true }, { "name": "sdh-Arab", "langs": [ "sdh" ], "noLang": true }, { "name": "hac-Arab", "langs": [ "hac" ], "noLang": true }, { "name": "ja-romanîzekirin", "langs": [ "ja" ], "noLang": true }, { "name": "ckb-Arab", "langs": [ "ckb" ], "noLang": true }, { "name": "awayekî din", "langs": null }, { "name": "guharto", "langs": null }, { "name": "hevmaneya", "langs": null }, { "name": "kevnbûyî", "langs": null }, { "name": "kêmpeyda", "langs": null }, { "name": "şaşnivîs", "langs": null } ], "jederDefs": { "jêder-kitêb": { "label": "Kitêb", "fields": [ { "key": "nivîskar", "label": "Nivîskar" }, { "key": "sernav", "label": "Sernav" }, { "key": "sernavê orjînal", "label": "Sernavê orjînal" }, { "key": "terciman", "label": "Terciman" }, { "key": "weşanxane", "label": "Weşanxane" }, { "key": "cih", "label": "Cih" }, { "key": "sal", "label": "Sal" }, { "key": "çap", "label": "Çap" }, { "key": "rûpel", "label": "Rûpel" }, { "key": "isbn", "label": "ISBN" }, { "key": "url", "label": "URL" } ] }, "jêder-kovar": { "label": "Kovar", "fields": [ { "key": "nivîskar", "label": "Nivîskar" }, { "key": "nivîskar2", "label": "Nivîskar 2" }, { "key": "sernav", "label": "Sernav" }, { "key": "sernavê orjînal", "label": "Sernavê orjînal" }, { "key": "kovar", "label": "Kovar" }, { "key": "terciman", "label": "Terciman" }, { "key": "weşanxane", "label": "Weşanxane" }, { "key": "cild", "label": "Cild" }, { "key": "hejmar", "label": "Hejmar" }, { "key": "sal", "label": "Sal" }, { "key": "rûpel", "label": "Rûpel" }, { "key": "url", "label": "URL" } ] }, "jêder-websîte": { "label": "Websîte", "fields": [ { "key": "nivîskar", "label": "Nivîskar" }, { "key": "sernav", "label": "Sernav" }, { "key": "terciman", "label": "Terciman" }, { "key": "websîte", "label": "Websîte" }, { "key": "url", "label": "URL" }, { "key": "arşîv", "label": "Arşîva URLê" }, { "key": "sal", "label": "Sal" }, { "key": "tarîx", "label": "Tarîx" } ] } }, "pronTpls": [ { "name": "ku-IPA", "desc": "IPAya otomatîk ji bo kurmancî (kontrol hewce ye)", "snippet": "* {{ku-IPA}}", "langs": [ "ku" ], "noLang": true }, { "name": "ku-kîte", "desc": "Hecekirina otomatîk ji bo kurmancî", "snippet": "* {{ku-kîte}}", "langs": [ "ku" ], "noLang": true }, { "name": "zza-IPA", "desc": "IPAya otomatîk ji bo zazakî", "snippet": "* {{zza-IPA}}", "langs": [ "zza" ], "noLang": true }, { "name": "zza-kîte", "desc": "Hecekirina otomatîk ji bo zazakî", "snippet": "* {{zza-kîte}}", "langs": [ "zza" ], "noLang": true }, { "name": "IPA", "desc": "Alfabeya fonetîk a navmiletî (IPA)", "snippet": "* {{IPA|CODE|/…/}}" }, { "name": "bilêv", "desc": "{{bilêv|herf|nerm, req|hejmar}}", "snippet": "* {{bilêv|herf|cure|hejmar}}" }, { "name": "kîte", "desc": "Şablona hecekirinê", "snippet": "* {{kîte|CODE|he|ce}}" }, { "name": "hevdeng", "desc": "Hevdeng — gotinên bi heman dengî", "snippet": "* {{hevdeng|CODE|…}}" } ], "etymTpls": [ { "name": "ji", "desc": "Ji zimanekî din hatiye — {{ji|en|ku|anatomy|tr=|t=}}", "snippet": "{{ji|SOURCELANG|CODE|…}}" }, { "name": "deyn", "desc": "Deynkirî — {{deyn|lang|kod|kelîme}}", "snippet": "{{deyn|SOURCELANG|CODE|…}}" }, { "name": "mîr", "desc": "Mîrasmayî — {{mîr|lang|kod|kelîme}}", "snippet": "{{mîr|SOURCELANG|CODE|…}}" }, { "name": "qertaf", "desc": "Ji bi pêşgir, paşgir û hevedudanî — {{qertaf|kod|pêşgir-|reh}}", "snippet": "{{qertaf|CODE|…|…}}" }, { "name": "q", "desc": "Referans bo rûpelekê — {{q|kod|kelîme}} navê zimanî dernakeve", "snippet": "{{q|CODE|…}}" }, { "name": "hv", "desc": "Hevreh — {{hv|lang|kelîme}} navê zimanî derdikeve", "snippet": "{{hv|SOURCELANG|…}}" }, { "name": "nh", "desc": "Nehevreh — {{nh|lang|kelîme}} navê zimanî derdikeve", "snippet": "{{nh|SOURCELANG|…}}" }, { "name": "cotreh", "desc": "Cotreh — teror {{cotreh|ku|tirs}}", "snippet": "{{cotreh|CODE|…}}" }, { "name": "yekser", "desc": "tercimeya yekser — {{yekser|lang|kod|kelîme}}", "snippet": "{{yekser|SOURCELANG|CODE|…}}" }, { "name": "wergirtin", "desc": "Wergirtina maneyê — {{wergirtin|lang|kod|kelîme}}", "snippet": "{{wergirtin|SOURCELANG|CODE|…}}" }, { "name": "paşvesazî", "desc": "Paşvesazî — greed {{paşvesazî|en|greedy}}", "snippet": "{{paşvesazî|CODE|…}}" }, { "name": "qusandin", "desc": "Qusandin — Miho {{qusandin|ku|Mihemmed}}", "snippet": "{{qusandin|CODE|…}}" }, { "name": "têkilkirin", "desc": "Têkilkirin — sormancî {{têkilkirin|ku|soranî|kurmancî}}", "snippet": "{{têkilkirin|CODE|…}}" }, { "name": "nyz", "desc": "Nayê zanîn — {{nyz|ku|hûr=1}}", "snippet": "{{nyz|CODE}}" }, { "name": "onom", "desc": "Onomatopoetîk — {{onom|ku|hûr=1}}", "snippet": "{{onom|CODE}}" }, { "name": "navmiletî", "desc": "Navmiletî — {{navmiletî|ku|hûr=1}}", "snippet": "{{navmiletî|CODE}}" }, { "name": "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", "mn", "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" } } } kfifx2ehwhh4h5yzcarsyl0cbdqiaw9 6291586 6291584 2026-07-26T18:03:20Z Wikihez 7344 6291586 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ê û nêr", "mnmane": "mê yan nêr li gorî maneyê", "g": "giştî", "y": "yekjimar", "p": "pirjimar" }, "tplDefs": { "awayê Jacobsonê": { "langs": [ "zza" ] }, "rastnivîs": { "langs": [ "ku" ], "noLang": true }, "ku-Latn": { "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 }, "awayekî din": { "langs": null }, "guharto": { "langs": null }, "hevmaneya": { "langs": null }, "kevnbûyî": { "langs": null }, "kêmpeyda": { "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|/…/}}" }, "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", "mn", "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" } } } j9i77gp3d50wh8e6rtma0tb8knfsavo Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js 2 1357853 6291573 6265090 2026-07-26T15:54:43Z Wikihez 7344 6291573 javascript text/javascript /** * Gadget-citoid-helper.js * * Amûreke hevpar ji bo standina daneyên çavkaniyê ji Citoid û * veguherandina wan bo qadên şablonên jêder-* (jêder-kitêb, jêder-kovar, jêder-websîte). * * BIKARANÎN JI SCRIPTEKE DIN: * * 1) Riya herî baş: vê rûpelê wek dependency lê zêde bike di eyarên * gadgeta xwe de li ser [[Special:Gadgets]] (an jî MediaWiki:Gadgets-definition), * paşê: * * mw.loader.using("ext.gadget.citoid-helper").then(function () { * wikt.citoid.fetchJederFields(url).then(function (result) { * console.log(result.jederKey, result.fields); * }); * }); * * 2) An jî, heke ne mimkun be gadget bi vî awayî were qeydkirin, fonksiyona * alîkar a `wikt.citoidHelper.ensure()` a li jêr bikar bîne, ku bixweber * vê rûpelê bar dike heke jixwe nehatibe barkirin: * * wikt.citoidHelper.ensure().then(function () { * wikt.citoid.fetchJederFields(url).then(...); * }); * * API ya `window.wikt.citoid`: * - getRestUrl() → {string} URLya REST a Citoid a vê wîkiyê * - yearFromDate(dateStr) → {string} sala (4 reqem) ji rêzikeke tarîxê * - formatAuthors(authors) → {string} navên nivîskaran wek rêzikek yekane * - typeToJederKey(itemType) → {string} "jêder-kitêb" / "jêder-kovar" / "jêder-websîte" * - dataToJederFields(jederKey, data) → {Object<string,string>} qonaxên şablona jêder-* * - fetchData(query) → {jQuery.Promise<Object>} daneya xam a Citoid * - fetchJederFields(query) → {jQuery.Promise<{jederKey, fields, raw}>} hêsankirî * - TYPE_TO_JEDER → {Object} xerîteya itemType → jederKey */ window.wikt = window.wikt || {}; (function () { "use strict"; // Heke jixwe hatibe barkirin, dubare neke. if (window.wikt.citoid) { return; } // Xerîteya cureyên Citoid (itemType) li ser şablonên me yên jêder-* var CITOID_TYPE_TO_JEDER = { "book": "jêder-kitêb", "bookSection": "jêder-kitêb", "thesis": "jêder-kitêb", "report": "jêder-kitêb", "manuscript": "jêder-kitêb", "journalArticle": "jêder-kovar", "magazineArticle": "jêder-kovar", "newspaperArticle": "jêder-kovar", "webpage": "jêder-websîte", "blogPost": "jêder-websîte", "forumPost": "jêder-websîte", "videoRecording": "jêder-websîte", "podcast": "jêder-websîte", "encyclopediaArticle": "jêder-websîte" }; /** * Ji mantiqa [[:en:w:User:BrandonXLF/Citoid.js]] * Vedigere URLya REST a Citoid, li gorî eyarên vê wîkiyê. * @return {string} */ function getRestUrl() { var cfg = mw.config.get("wgCitoidConfig"); var vecfg = mw.config.get("wgVisualEditorConfig"); return (cfg && (cfg.fullRestbaseUrl || cfg.wbFullRestbaseUrl)) ? (cfg.fullRestbaseUrl || cfg.wbFullRestbaseUrl) + "v1/data/citation/mediawiki/" : (vecfg && vecfg.fullRestbaseUrl) ? vecfg.fullRestbaseUrl + "v1/data/citation/mediawiki/" : (cfg && cfg.citoidServiceUrl) ? cfg.citoidServiceUrl + "/mediawiki/" : "https://ku.wiktionary.org/api/rest_v1/data/citation/mediawiki/"; } /** * Ji tarîxa Citoid (rêzik an tarîxeke ISO) sala wê (4 reqem) derdixe. * @param dateStr {string} * @return {string} */ function yearFromDate(dateStr) { if (!dateStr) { return ""; } var match = /\d{4}/.exec(dateStr); return match ? match[0] : ""; } /** * Navên nivîskaran ên Citoid (rêzek [pêşnav, paşnav]) dike rêzikek yekane. * @param authors {Array<Array<string>>} * @return {string} */ function formatAuthors(authors) { if (!Array.isArray(authors)) { return ""; } return authors.map(function (pair) { return (pair || []).filter(function (part) { return !!part; }).join(" "); }).filter(function (name) { return !!name; }).join("; "); } function formatEditors(editors) { if (!Array.isArray(editors)) { return ""; } return editors.map(function (pair) { return (pair || []).filter(function (part) { return !!part; }).join(" "); }).filter(function (name) { return !!name; }).join("; "); } /** * Li gorî itemType a Citoid, navê şablona jêder-* a munasi vedigerîne. * @param itemType {string} * @return {string} Yek ji "jêder-kitêb", "jêder-kovar" an "jêder-websîte". */ function typeToJederKey(itemType) { return CITOID_TYPE_TO_JEDER[itemType] || "jêder-websîte"; } /** * Daneyên Citoid vediguherîne tabloyeke parametreyên şablona jêder-* (li gorî cureyê wê). * @param jederKey {string} Yek ji "jêder-kitêb", "jêder-kovar" an "jêder-websîte". * @param data {Object} Netîceya Citoid. * @return {Object<string, string>} */ function dataToJederFields(jederKey, data) { var out = {}; var author = formatAuthors(data.author); var editor = formatEditors(data.editor); if (author) { out["nivîskar"] = author; } if (editor) { out["amadekar"] = editor; } if (data.title) { out["sernav"] = data.title; } if (data.url) { out.url = data.url; } if (jederKey === "jêder-kitêb") { if (data.publisher) { out["weşanxane"] = data.publisher; } if (data.place) { out.cih = data.place; } if (data.date) { out.sal = yearFromDate(data.date); } if (data.ISBN) { out.isbn = Array.isArray(data.ISBN) ? data.ISBN[0] : data.ISBN; } if (data.edition) { out["çap"] = data.edition; } if (data.pages) { out["rûpel"] = data.pages; } } else if (jederKey === "jêder-kovar") { if (data.publicationTitle) { out.kovar = data.publicationTitle; } if (data.publisher) { out["weşanxane"] = data.publisher; } if (data.volume) { out.cild = data.volume; } if (data.issue) { out.hejmar = data.issue; } if (data.pages) { out["rûpel"] = data.pages; } if (data.date) { out.sal = yearFromDate(data.date); } } else if (jederKey === "jêder-websîte") { if (data.websiteTitle) { out["websîte"] = data.websiteTitle; } if (data.date) { out["tarîx"] = data.date; } } return out; } /** * Ji Citoid daneyên çavkaniyekê tîne (li gorî URL, DOI an ISBNyê). * @param query {string} * @return {jQuery.Promise} Bi daneya ewil a Citoid (Object) tê safîkirin. */ function fetchData(query) { var url; try { url = encodeURIComponent(decodeURIComponent(query)); } catch (e) { url = encodeURIComponent(query); } return $.get(getRestUrl() + url).then(function (res) { if (!res || !res[0]) { return $.Deferred().reject("Netîceya ji Citoidê vala ye.").promise(); } return res[0]; }); } /** * Fonksiyoneke hêsankirî: ji URL/DOI/ISBNyekê rasterast şablona jêder-* a munasib * û qonaxên wê yên tijîkirî vedigerîne (fetchData + typeToJederKey + dataToJederFields * bi hev re). * @param query {string} * @return {jQuery.Promise} Bi {jederKey: string, fields: Object<string,string>, raw: Object} tê safîkirin. */ function fetchJederFields(query) { return fetchData(query).then(function (data) { var jederKey = typeToJederKey(data.itemType); return { jederKey: jederKey, fields: dataToJederFields(jederKey, data), raw: data }; }); } window.wikt.citoid = { getRestUrl: getRestUrl, yearFromDate: yearFromDate, formatAuthors: formatAuthors, typeToJederKey: typeToJederKey, dataToJederFields: dataToJederFields, fetchData: fetchData, fetchJederFields: fetchJederFields, TYPE_TO_JEDER: CITOID_TYPE_TO_JEDER }; /** * Alîkar ji bo scriptên din: heke wikt.citoid hîn nehatibe barkirin, * vê rûpelê bixweber bar dike û pey re "resolve" dibe. * Bikaranîn: wikt.citoidHelper.ensure().then(function() { ... wikt.citoid ... }); */ window.wikt.citoidHelper = { ensure: function () { if (window.wikt.citoid) { return $.Deferred().resolve().promise(); } var url = mw.util.getUrl("MediaWiki:Gadget-citoid-helper.js", { action: "raw", ctype: "text/javascript" }); return mw.loader.getScript(mw.config.get("wgServer") + url); } }; })(); i1a3i5jf7t29mnd9m9vurpt3e5ha0jt 6291574 6291573 2026-07-26T15:57:25Z Wikihez 7344 6291574 javascript text/javascript /** * Gadget-citoid-helper.js * * Amûreke hevpar ji bo standina daneyên çavkaniyê ji Citoid û * veguherandina wan bo qadên şablonên jêder-* (jêder-kitêb, jêder-kovar, jêder-websîte). * * BIKARANÎN JI SCRIPTEKE DIN: * * 1) Riya herî baş: vê rûpelê wek dependency lê zêde bike di eyarên * gadgeta xwe de li ser [[Special:Gadgets]] (an jî MediaWiki:Gadgets-definition), * paşê: * * mw.loader.using("ext.gadget.citoid-helper").then(function () { * wikt.citoid.fetchJederFields(url).then(function (result) { * console.log(result.jederKey, result.fields); * }); * }); * * 2) An jî, heke ne mimkun be gadget bi vî awayî were qeydkirin, fonksiyona * alîkar a `wikt.citoidHelper.ensure()` a li jêr bikar bîne, ku bixweber * vê rûpelê bar dike heke jixwe nehatibe barkirin: * * wikt.citoidHelper.ensure().then(function () { * wikt.citoid.fetchJederFields(url).then(...); * }); * * API ya `window.wikt.citoid`: * - getRestUrl() → {string} URLya REST a Citoid a vê wîkiyê * - yearFromDate(dateStr) → {string} sala (4 reqem) ji rêzikeke tarîxê * - formatAuthors(authors) → {string} navên nivîskaran wek rêzikek yekane * - typeToJederKey(itemType) → {string} "jêder-kitêb" / "jêder-kovar" / "jêder-websîte" * - dataToJederFields(jederKey, data) → {Object<string,string>} merheleyên şablona jêder-* * - fetchData(query) → {jQuery.Promise<Object>} daneya xam a Citoid * - fetchJederFields(query) → {jQuery.Promise<{jederKey, fields, raw}>} hêsankirî * - TYPE_TO_JEDER → {Object} xerîteya itemType → jederKey */ window.wikt = window.wikt || {}; (function () { "use strict"; // Heke jixwe hatibe barkirin, dubare neke. if (window.wikt.citoid) { return; } // Xerîteya cureyên Citoid (itemType) li ser şablonên me yên jêder-* var CITOID_TYPE_TO_JEDER = { "book": "jêder-kitêb", "bookSection": "jêder-kitêb", "thesis": "jêder-kitêb", "report": "jêder-kitêb", "manuscript": "jêder-kitêb", "journalArticle": "jêder-kovar", "magazineArticle": "jêder-kovar", "newspaperArticle": "jêder-kovar", "webpage": "jêder-websîte", "blogPost": "jêder-websîte", "forumPost": "jêder-websîte", "videoRecording": "jêder-websîte", "podcast": "jêder-websîte", "encyclopediaArticle": "jêder-websîte" }; /** * Ji mantiqa [[:en:w:User:BrandonXLF/Citoid.js]] * Vedigere URLya REST a Citoid, li gorî eyarên vê wîkiyê. * @return {string} */ function getRestUrl() { var cfg = mw.config.get("wgCitoidConfig"); var vecfg = mw.config.get("wgVisualEditorConfig"); return (cfg && (cfg.fullRestbaseUrl || cfg.wbFullRestbaseUrl)) ? (cfg.fullRestbaseUrl || cfg.wbFullRestbaseUrl) + "v1/data/citation/mediawiki/" : (vecfg && vecfg.fullRestbaseUrl) ? vecfg.fullRestbaseUrl + "v1/data/citation/mediawiki/" : (cfg && cfg.citoidServiceUrl) ? cfg.citoidServiceUrl + "/mediawiki/" : "https://ku.wiktionary.org/api/rest_v1/data/citation/mediawiki/"; } /** * Ji tarîxa Citoid (rêzik an tarîxeke ISO) sala wê (4 reqem) derdixe. * @param dateStr {string} * @return {string} */ function yearFromDate(dateStr) { if (!dateStr) { return ""; } var match = /\d{4}/.exec(dateStr); return match ? match[0] : ""; } /** * Navên nivîskaran ên Citoid (rêzek [pêşnav, paşnav]) dike rêzikek yekane. * @param authors {Array<Array<string>>} * @return {string} */ function formatAuthors(authors) { if (!Array.isArray(authors)) { return ""; } return authors.map(function (pair) { return (pair || []).filter(function (part) { return !!part; }).join(" "); }).filter(function (name) { return !!name; }).join("; "); } function formatEditors(editors) { if (!Array.isArray(editors)) { return ""; } return editors.map(function (pair) { return (pair || []).filter(function (part) { return !!part; }).join(" "); }).filter(function (name) { return !!name; }).join("; "); } /** * Li gorî itemType a Citoid, navê şablona jêder-* a munasib vedigerîne. * @param itemType {string} * @return {string} Yek ji "jêder-kitêb", "jêder-kovar" an "jêder-websîte". */ function typeToJederKey(itemType) { return CITOID_TYPE_TO_JEDER[itemType] || "jêder-websîte"; } /** * Daneyên Citoid vediguherîne tabloyeke parametreyên şablona jêder-* (li gorî cureyê wê). * @param jederKey {string} Yek ji "jêder-kitêb", "jêder-kovar" an "jêder-websîte". * @param data {Object} Netîceya Citoid. * @return {Object<string, string>} */ function dataToJederFields(jederKey, data) { var out = {}; var author = formatAuthors(data.author); var editor = formatEditors(data.editor); if (author) { out["nivîskar"] = author; } if (editor) { out["amadekar"] = editor; } if (data.title) { out["sernav"] = data.title; } if (data.url) { out.url = data.url; } if (jederKey === "jêder-kitêb") { if (data.publisher) { out["weşanxane"] = data.publisher; } if (data.place) { out.cih = data.place; } if (data.date) { out.sal = yearFromDate(data.date); } if (data.ISBN) { out.isbn = Array.isArray(data.ISBN) ? data.ISBN[0] : data.ISBN; } if (data.edition) { out["çap"] = data.edition; } if (data.pages) { out["rûpel"] = data.pages; } } else if (jederKey === "jêder-kovar") { if (data.publicationTitle) { out.kovar = data.publicationTitle; } if (data.publisher) { out["weşanxane"] = data.publisher; } if (data.volume) { out.cild = data.volume; } if (data.issue) { out.hejmar = data.issue; } if (data.pages) { out["rûpel"] = data.pages; } if (data.date) { out.sal = yearFromDate(data.date); } } else if (jederKey === "jêder-websîte") { if (data.websiteTitle) { out["websîte"] = data.websiteTitle; } if (data.date) { out["tarîx"] = data.date; } } return out; } /** * Ji Citoid daneyên çavkaniyekê tîne (li gorî URL, DOI an ISBNyê). * @param query {string} * @return {jQuery.Promise} Bi daneya ewil a Citoid (Object) tê safîkirin. */ function fetchData(query) { var url; try { url = encodeURIComponent(decodeURIComponent(query)); } catch (e) { url = encodeURIComponent(query); } return $.get(getRestUrl() + url).then(function (res) { if (!res || !res[0]) { return $.Deferred().reject("Netîceya ji Citoidê vala ye.").promise(); } return res[0]; }); } /** * Fonksiyoneke hêsankirî: ji URL/DOI/ISBNyekê rasterast şablona jêder-* a munasib * û merheleyên wê yên tijîkirî vedigerîne (fetchData + typeToJederKey + dataToJederFields * bi hev re). * @param query {string} * @return {jQuery.Promise} Bi {jederKey: string, fields: Object<string,string>, raw: Object} tê safîkirin. */ function fetchJederFields(query) { return fetchData(query).then(function (data) { var jederKey = typeToJederKey(data.itemType); return { jederKey: jederKey, fields: dataToJederFields(jederKey, data), raw: data }; }); } window.wikt.citoid = { getRestUrl: getRestUrl, yearFromDate: yearFromDate, formatAuthors: formatAuthors, typeToJederKey: typeToJederKey, dataToJederFields: dataToJederFields, fetchData: fetchData, fetchJederFields: fetchJederFields, TYPE_TO_JEDER: CITOID_TYPE_TO_JEDER }; /** * Alîkar ji bo scriptên din: heke wikt.citoid hîn nehatibe barkirin, * vê rûpelê bixweber bar dike û pey re "resolve" dibe. * Bikaranîn: wikt.citoidHelper.ensure().then(function() { ... wikt.citoid ... }); */ window.wikt.citoidHelper = { ensure: function () { if (window.wikt.citoid) { return $.Deferred().resolve().promise(); } var url = mw.util.getUrl("MediaWiki:Gadget-citoid-helper.js", { action: "raw", ctype: "text/javascript" }); return mw.loader.getScript(mw.config.get("wgServer") + url); } }; })(); hzym8pr8qmqgre968ao6new00hqiz5a Wîkîferheng:Xebat 4 1357857 6291575 6290823 2026-07-26T15:58:53Z Wikihez 7344 /* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re 6291575 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) = 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) == [[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.js/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) == [[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) == 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) == Gadget-linkify == * [[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) == 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: <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. 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) = Moda tarî = == MediaWiki:Newarticletext == * [[MediaWiki:Newarticletext]]: Divê rengên wê were rastkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == Modul:dara kategoriyan == * [[Modul:dara kategoriyan]] link_box —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == mane? == * {{ş|mane?}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == bêçavkanî == * {{ş|bêçavkanî}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == de-tew-nerm-ern == * {{ş|de-tew-nerm-ern}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 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) == 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) == [[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) hko2we2yrodp8zjeeexikrzexzber6l 6291576 6291575 2026-07-26T16:58:56Z Wikihez 7344 /* Mediawiki:Gadget-citoid-helper.js */ 6291576 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) = 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î? # [[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.js/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) == [[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) == 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) == Gadget-linkify == * [[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) == 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: <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. 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) = Moda tarî = == MediaWiki:Newarticletext == * [[MediaWiki:Newarticletext]]: Divê rengên wê were rastkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == Modul:dara kategoriyan == * [[Modul:dara kategoriyan]] link_box —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == mane? == * {{ş|mane?}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == bêçavkanî == * {{ş|bêçavkanî}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == de-tew-nerm-ern == * {{ş|de-tew-nerm-ern}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 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) == 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) == [[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) gkihvkbtj0bhz1cq4kxri276u3le0bv 6291599 6291576 2026-07-26T20:08:52Z Wikihez 7344 /* MediaWiki:Newarticletext */ Bersiv 6291599 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) = 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î? # [[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.js/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) == [[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) == 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) == Gadget-linkify == * [[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) == 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: <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. 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) = Moda tarî = == MediaWiki:Newarticletext == * [[MediaWiki:Newarticletext]]: Divê rengên wê were rastkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) *:{{çêbû}} çavê min kor dikir wextê [[WF:Nû]] rast dikim {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:08, 26 tîrmeh 2026 (UTC) == Modul:dara kategoriyan == * [[Modul:dara kategoriyan]] link_box —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == mane? == * {{ş|mane?}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == bêçavkanî == * {{ş|bêçavkanî}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == de-tew-nerm-ern == * {{ş|de-tew-nerm-ern}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 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) == 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) == [[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) kmfk9luj8v40him5k6obxaov9ylg1ua 6291620 6291599 2026-07-26T23:24:16Z Wikihez 7344 /* Modul:dara kategoriyan */ Bersiv 6291620 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) = 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î? # [[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.js/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) == [[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) == 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) == Gadget-linkify == * [[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) == 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: <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. 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) = Moda tarî = == MediaWiki:Newarticletext == * [[MediaWiki:Newarticletext]]: Divê rengên wê were rastkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) *:{{çêbû}} çavê min kor dikir wextê [[WF:Nû]] rast dikim {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:08, 26 tîrmeh 2026 (UTC) == Modul:dara kategoriyan == * [[Modul:dara kategoriyan]] link_box —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) *:{{çêbû}} feqat [[Modul:dara kategoriyan/zimankatbike]] çênabe. Claude serê xwar dîsa nebû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:24, 26 tîrmeh 2026 (UTC) == mane? == * {{ş|mane?}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == bêçavkanî == * {{ş|bêçavkanî}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == de-tew-nerm-ern == * {{ş|de-tew-nerm-ern}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 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) == 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) == [[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) 8mzh8fyvl7ebn7vl5beviwmez73nwb2 6291622 6291620 2026-07-26T23:35:21Z Wikihez 7344 /* Modul:dara kategoriyan */ Bersiv 6291622 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) = 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î? # [[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.js/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) == [[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) == 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) == Gadget-linkify == * [[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) == 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: <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. 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) = Moda tarî = == MediaWiki:Newarticletext == * [[MediaWiki:Newarticletext]]: Divê rengên wê were rastkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) *:{{çêbû}} çavê min kor dikir wextê [[WF:Nû]] rast dikim {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:08, 26 tîrmeh 2026 (UTC) == Modul:dara kategoriyan == * [[Modul:dara kategoriyan]] link_box —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) *:{{çêbû}} feqat [[Modul:dara kategoriyan/zimankatbike]] çênabe. Claude serê xwar dîsa nebû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:24, 26 tîrmeh 2026 (UTC) *::ew jî çêbû [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:35, 26 tîrmeh 2026 (UTC) == mane? == * {{ş|mane?}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == bêçavkanî == * {{ş|bêçavkanî}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC) == de-tew-nerm-ern == * {{ş|de-tew-nerm-ern}} —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 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) == 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) == [[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) ca551i4o9ck3bz3kw0jzwb0uh6azqxm Wîkîferheng:ceribandin/nimûne 4 1357865 6291562 6291543 2026-07-26T14:08:26Z Wikihez 7344 6291562 wikitext text/x-wiki {{/vê rêzê neguhêre}} == {{ziman|en}} == === Lêker === [[Wêne:Example.jpg|thumb|Mînak]] {{lêker|en}} # Maneya 1ê. #: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}} #* {{jêder-kovar/ceribandin|en|nivîskar=Justin Chang|lînka-nivîskar=Justin Chang|sernav=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|urlya-arşîvê=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|tarîxa-arşîvê=28 June 2017|kovar=[[w:Variety (magazine)|Variety]]|tarîx=25 May 2011|issn=0042-2738|oclc=960911146|jêgirtin=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}} #* {{jêder-kitêb/ceribandin|en|nivîskar=F[rancis] Scott Fitzgerald|lînka-nivîskar=F. Scott Fitzgerald|beş=IX|sernav=[[w:The Great Gatsby|The Great Gatsby]]|cih=New York, N.Y.|weşanxane=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|sal=1925|oclc=884653065|cih2=New York, N.Y.|weşanxane2=Charles Scribner’s Sons|sal2=1953|isbn2=978-0-684-16498-4|rûpel2=182|urlya-rûpelê2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|jêgirtin=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}} #* {{jêder-malper/ceribandin|en|nivîskar=Meg Downey|sernav=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|urlya-arşîvê=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|tarîxa-arşîvê=24 June 2018|xebat=[[w:Polygon (website)|Polygon]]|tarîx=7 May 2018|jêgirtin=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}} #* {{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=The Republic of Molossia has played a considerable role in developing '''intermicronational''' diplomacy.}} #* {{jêder-kitêb/ceribandin|en |sal=c. 1988|nivîskar=Peter W. Rodino, Jr.; et al.|sernav=Report of the Congressional Committees Investigating the Iran/Contra Affair|urlya-rûpelê=http://books.google.com/books?id=ew_K3auTwEgC&pg=PA646&dq=phone+call|rûpel=646|jêgirtin=On November 20, they had a '''phone call''' and later a meeting with others lasting at least 1 1/2 hours.}} #* {{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=Centrally acting '''sympathicolytics''' cause a decrease in the sympathetic outflow from the brainstem. This reduction in sympathetic outflow brings about vasodilatation of the peripheral vascular bed with a concomitant drop in the blood pressure}} #* {{jêder-kitêb/ceribandin|en |sal=2005|edîtor=Martin Endress; George Psathas; Hisashi Nasu|beş=Between the Everyday Life-World and the World of Social Scientific Theory—Towards an “Adequate” Social Thory|nivîskar=Hisashi Nasu|sernav=Explorations of the Life-World: Continuing Dialogues with Alfred Schutz|weşanxane=Springer|isbn=1402032196|rûpel=136|jêgirtin=It is not only the '''man on the street''' who is characterized by taking one's own system of relevance for granted. This holds true for the expert,....}} #* {{jêder-kitêb/ceribandin|en |sal=2002|nivîskar=Peter Shirlow|beş=Sinn Féin: Beyond; within Containment|hejmara-beşê=4|edîtor=Stefan Wolff; Jörg Neuheiser|sernav=Peace at Last?: The Impact of the Good Friday Agreement on Northern Ireland|weşanxane=Berghahn Books|isbn=157181518X|urlya-rûpelê=http://books.google.com/books?id=QyInASKCFjcC&pg=PA62&dq=ethnosectarian&sig=Q54T1Vz3vsKnqd05666N1ddFki8|rûpel=62|jêgirtin=Moreover, language, discourse, and dialogue, their definition and mobilisation, create chains of equivalence which enclose subjects within not only '''ethnosectarian''' but at times entirely self-referential and self-sustained notions of identity, practice and collective devotion.}} #* {{jêder-kitêb/ceribandin|en |sernav=Extreme Hydrological Events: New Concepts for Security|edîtor=O.F. Vasiliev; P.H.A.J.M. van Gelder; E.J. Plate; M.V. Bolgov|sal=2007|weşanxane=[[w:Springer Science+Business Media|Springer]]|isbn=1402057393|beş=Predetermination of Floods|nivîskar=Pierre Hubert; Ioulia Tchiguirinskaia; Daniel Schertzer; Hocine Bendjoudi; Shaun Lovejoy|qisim=On Probability and Return Periods of Floods|hejmara-qismê=2|rûpel=187|jêgirtin=It is perfectly legitimate to be interested, particularly for safety studies, by millennium floods or floods or '''decamillennium''' floods (i.e. having a probability 0,001 and 0,0001 respectively to occur in the course of given year) of a river which did not exist five thousand years ago and that will not exist perhaps any more in ten thousand years, similarly to the industrial interest in the probability of faults which will not undoubtedly occur, because the minimization of their probability is an aspect of a policy of industrial security.}} #* {{jêder-kitêb/ceribandin|en |sal=1995|nivîskar=w:Robert M. Solovay|nivîskarê-sereke=w:Kurt Gödel|edîtor=w:Solomon Feferman; w:John W. Dawson Jr.; w:Warren Goldfarb; {{w|Charles Parsons (philosopher)|Charles Parsons}}; w:Robert M. Solovay|beş=Notes to *1970 &#91;{{w|Godel's ontological proof}}&#93;|sernav=Kurt Gödel: Collected Works: Volume III|urlya-rûpelê=https://books.google.com.au/books?id=gDzbuUwma5MC&pg=PA419&dq=%22Hausdorff+gap%22%7C%22Hausdorff+gaps%22&hl=en&newbks=1&newbks_redir=0&sa=X&ved=2ahUKEwjo-o7D9OT7AhVQJUQIHaSlBIgQ6AF6BAhXEAI#v=onepage&q=%22Hausdorff%20gap%22%7C%22Hausdorff%20gaps%22&f=false|rûpel=419|weşanxane=w:Oxford University Press|jêgirtin=Of course, Hausdorff did not talk of models of set theory or prove absoluteness results. He gave a direct construction in ''ZFC'' of what is now called a '''Hausdorff gap''' and proved properties 1 through 4 for his construction. The notion of a '''Hausdorff gap''' and the proof that '''Hausdorff gaps''' are indestructible under cardinal preserving extensions (property 5 above) are due to Kunen (unpublished).}} #* {{jêder-kitêb/ceribandin|en |sal=1925|nivîskar={{w|F. Scott Fitzgerald|F[rancis] Scott Fitzgerald}}|beş=IX|sernav=w:The Great Gatsby|cih=New York, N.Y.|weşanxane=w:Charles Scribner's Sons|oclc=884653065|sal2=1953|cih2=New York, N.Y.|weşanxane2=Charles Scribner’s Sons|isbn2=978-0-684-16498-4|rûpel2=182|urlya-rûpelê2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|jêgirtin=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.|lccn=gm 95-1}} #* {{jêder-kitêb/ceribandin|en |nivîskar=w:John Boyle O'Reilly|beş=On the Trail|sernav=[[w:Moondyne|Moondyne: A Story from the Under-world]]|cih=London|weşanxane=[[w:Routledge|George Routledge and Sons]]|sal=1878|sala-weşandinê=1879|oclc=39983928|sernav2=Moondyne: A Story of Convict Life in Australia|cih2=London|weşanxane2=George Routledge & Sons, Limited, Broadway House, {{w|Ludgate Hill}}|sal2=[1880s]|qisim2=book first|pages2=23–24|urlya-rûpelê2=https://archive.org/stream/moondynestory00oreirich#page/24/mode/1up/|oclc2=83033698|jêgirtin=It was sore travelling for horse and man under the blazing sun, with no food nor water save what he pressed from the pith of the palms, and even these were growing scarce. The only life on the plains was the hard and dusty scrub. Every hour brought a more hopeless and '''grislier''' desolation.}} #* {{jêder-kitêb/ceribandin|en |sal=1966|sala-orîjînal=1858|nivîskar=w:Hans Christian Andersen|terciman=Pat Shaw Iversen|orîjînal=Suppe paa en Pølsepind|zimanê-orîjînal=da|beş=Soup from a Sausage Peg|sernav=The Snow Queen and Other Tales|serî=Signet Classic|cildê-seriyê=CT334|cih=New York, N.Y.|weşanxane=w:New American Library|rûpel=224|urlya-rûpelê=http://books.google.com/books?id=2tjWAAAAMAAJ&q=%22from+your+own+nook+and+cranny%22#search_anchor|oclc=636818779|metna-orîjînal=da:Det er underligt at komme hjemme fra sin egen '''Krinkelkrog''', gaae med Skib, der ogsaa er en Slags '''Krinkelkrog''', og saa pludselig være over hundrede Mile borte og staae i et fremmedt Land.|jêgirtin=It’s strange to come away from home, from your own '''nook and cranny''', to go by ship – which is also a kind of '''nook and cranny''' – and then suddenly be more than a hundred miles away and stand in a foreign land!}} #* {{jêder-kitêb/ceribandin|en |sal=2013|nivîskar=Bernd Klauer; Reiner Manstetten; Thomas Petersen; Johannes Schiller|zimanê-eserê=de|beş=Materielle Bestände|sernav=Die Kunst, langfristig zu denken : Wege zur Nachhaltigkeit<t:The art of thinking long-term : paths to sustainability>|cih=Baden-Baden|weşanxane=Nomos|versiyona-nû=translated as|terciman2=Kathleen Cross|sal2=2016|sernav2=Sustainability and the Art of Long-Term Thinking|beş2=Material stocks|cih2=London; New York|weşanxane2=Routledge, Taylor & Francis Group|qisim2=4.3.1. Material sets and stocks|rûpel2=44|isbn2=9781134986187|jêgirtin=The property of '''appertainment''' of a set is therefore the property which, by definition, has to be given in every element of the set.}} #* {{jêder-kitêb/ceribandin|en |nivîskar={{w|Andrew Seth Pringle-Pattison|Andrew Seth}}|sernav=Hegelianism and Personality|çap=2nd|sal=1887|sala-weşandinê=1893|rûpel=216|rêz-sade=footnote|cih=Edinburgh; London|weşanxane=William Blackwood and Sons|versiyona-nû=quoted in|sal2=1893|2nivîskar=w:David George Ritchie|sernav2=Darwin and Hegel, with other Philosophical Studies|cih2=London|weşanxane2=S. Sonnenschein & Co.; New York, N.Y.: [[w:Macmillan Publishers (United States)|Macmillan & Co.]]|rûpel2=72|urlya-rûpelê2=http://books.google.com/books?id=8yGEAHvAwiIC&q=%22An+absolute+system+cannot+afford+to+leave+any+nook+or+cranny+of+existence+unexplored%22#search_anchor|rêz-sade2=note 1|oclc2=3299658|jêgirtin=An absolute system cannot afford to leave '''any nook or cranny''' of existence unexplored.}} #* {{jêder-kitêb/ceribandin|en |nivîskar=w:David George Ritchie|jê-tê-girtin=w:Andrew Seth Pringle-Pattison|sernav=Hegelianism and Personality|çap=2nd|kesên-din=1893, page 216, footnote|cihê-jêgirtinê=Darwin and Hegel, with other Philosophical Studies|cih=London|weşanxane=S. Sonnenschein & Co.; New York, N.Y.: [[w:Macmillan Publishers (United States)|Macmillan & Co.]]|sal=1893|rûpel=72|rêz-sade=note 1|urlya-rûpelê=http://books.google.com/books?id=8yGEAHvAwiIC&q=%22An+absolute+system+cannot+afford+to+leave+any+nook+or+cranny+of+existence+unexplored%22#search_anchor|oclc=3299658|jêgirtin=An absolute system cannot afford to leave '''any nook or cranny''' of existence unexplored.}} #* {{jêder-kovar/ceribandin|en |nivîskar=w:Hayden Carruth|sernav=Making It New|kovar=w:The Hudson Review|cih=New York, N.Y.|weşanxane=Hudson Review, Inc.|meh=Summer|sal=1968|cild=XXI|hejmar=2|issn=2325-5935|oclc=920393805|versiyona-nû=reprinted as|cure2=book|beş2=From ‘Making It New’ [''Body Rags'']|edîtor2=Howard Nelson|sernav2=On the Poetry of Galway Kinnell: The Wages of Dying|serî2=Under Discussion|cih2=Ann Arbor, Mich.|weşanxane2={{w|University of Michigan Press}}|sal2=1987|rûpel2=75|urlya-rûpelê2=https://books.google.com/books?id=7cXV26PmJUYC&pg=PA75|isbn2=978-0-472-09376-2|jêgirtin=In his &#91;{{w|Galway Kinnell}}'s&#93; new book, ''Body Rags'', he has brought this style to a kind of perfection, especially in two poems about the killing of animals, "The Porcupine" and "The Bear." These are the '''grisliest''' poems I have ever read.}} #* {{jêder-kitêb/ceribandin|fr |sal=1973|nivîskar=w:Claude Simon|sernav=Tryptique|weşanxane=Éditions de Minuit|rûpel=12|jêgirtin=Les sons de la cloche '''égrenant''' les quarts, les demies et les heures {{...}}|t=The sounds of the clock '''rattling off''' the quarters, the halves and the hours {{...}}}} #* {{jêder-kitêb/ceribandin|de |sal=1999|nivîskar=Paul Hoffmann|hevnivîskar=Stefan H. Brandenburger; Ulrike Brauner; Thomas Hieke|sernav=Q 22:28, 30: You Will Judge the Twelve Tribes of Israel|serî=Documenta Q: Reconstructions of Q Through Two Centuries of Gospel Research Excerpted, Sorted and Evaluated|cildê-seriyê=volume 5|isbn=90-429-0734-7|rûpel=189|jêgirtin=Zumal in beiden Fällen eine Partizipialform vorliegt, liegt es nahe, die '''Zeitworte''' als Varianten anzusehen.}} #* {{jêder-kitêb/ceribandin|ru |nivîskar=lw:ru:[[Филарет (Дроздов)|Митрополи́т Филаре́т]]<tr:-><t:Mitropolit Filaret>|beş=ru:Сло́во предъ приведеніемъ къ присягѣ Санктпетербурксаго дворя́нства и гражданъ для избранія суде́й|tarîx=1818-02-01<!--Converted to Gregorian-->|sala-weşandinê=1873|cih=Moscow|sernav=ru:Сочинеиія Филарета Митрополита Московскаго и Коломенскаго|cild=I|rûpel=253|urlya-rûpelê=https://books.google.com/books?id=yWEcAAAAMAAJ&pg=PA253|jêgirtin=[[изъ|Изъ]] [[сей|сего́]] [[ви́дно]], [[что]] [[судья|судія́]], [[как|какъ]] [[блюсти́тель]] [[общественный|обще́ственнаго]] [[порядок|поря́дка]], [[должен|долженъ]] '''объима́ть''' [[и]] [[проника́ть]] [[мысль|мы́слію]] [[весь]] [[состав|соста́въ]] [[общество|о́бщества]];|t=From this it becomes discernible that a judge, in his function as a warden of public order, should '''enclose''' and traverse the whole body of the society;}} #* {{jêder-kitêb/ceribandin|yi |nivîskar=I.J. Singer|sernav=Fun a velt vos iz nishto mer|sal=1946|beş=yi:אַ ייד אַ מלמד פֿאַרגלוסט זיך צו ווערן אַ מלאך אום פּורים און פֿאַרלירט, נעבעך, אַן אויג<subst:ייד/ייִד,מלמד/מעלאַמעד,פּורים/פּורעם,מלאך/מאַלעך><t:A Jew, a teacher, decides to become an angel on Purim and, unfortunately, loses an eye>|jêgirtin=יעדן '''זמן''' אויפֿסניי, חול־המועד סוכות אָדער חול־המועד פּסח, בין איך געווען זייער האָפֿערדיק, וואָס מען האָט מיך גענומען איין מלמד און אָנגעדונגען צו אַן אַנדערן, גלײבנדיק, אַז דער נייער וועט זיין בעסער ווי דער אַלטער.|subst=זמן//זמאַן,חול־המועד//כאַלעמויעד,סוכות//סוקוס,פּסח//פּייסעך,מלמד//מעלאַמעד,נייער//נײַער|tercime=Every '''term''' anew, during the intermediary periods of Sukkot and Pesach, I was very confident in being taken from one melamed and given to another, believing the new one would be better than the old.}} #* {{jêder-kitêb/ceribandin|ru |sal=1983|nivîskar=w:Samuil Marshak|sernav=Баллада о королевском бутерброде<subst:бутер/бутэр><t:Ballad of the King's Bread>|orîjînal=The King's Breakfast|ji-aliyê=w:A. A. Milne|metna-orîjînal=en:"Nobody," / He whimpered, / "Could call me / A fussy man; / I only want / A little bit / Of butter for / My bread!"|zimanê-orîjînal=en|jêgirtin=— [[никто|Никто́]] [[не]] [[сказать|ска́жет]], [[бу́дто]] [[я]] / [[тиран|Тира́н]] [[и]] [[сумасбро́д]], / [[за|За]] [[то́]], [[что]] [[к]] [[чай|ча́ю]] [[я]] [[любить|люблю́]] / [[хороший|Хоро́ший]] '''бутербро́д'''.|subst=бутербро́д/бутэрбро́д|t="No one would say that I / Am a tyrant and madman, / For the fact that with my tea I like / A good '''bread-and-butter sandwich'''."}} #* {{jêder-kitêb/ceribandin|ota |sal=1922|sernav=ota:ادارهٔ حرب و سیاست<tr:Idare-i harb ve siyaset>|cih=Istanbul|weşanxane=ota:مطبعهٔ عثمانیه|rûpel=176|urlya-rûpelê=https://babel.hathitrust.org/cgi/pt?id=uc1.aa0006749931&view=1up&seq=182|versiyona-nû=translation of|ziman2=de|sal2=1922|2nivîskar=w:Erich Ludendorff|sernav2=Kriegsführung und Politik|cih2=Berlin|weşanxane2=E. S. Mittler & Sohn|rûpel2=199|urlya-rûpelê2=https://archive.org/details/kriegfhrungund00lude/page/199|jêgirtin=شرقی غالیچیا، و بوقوونیاده شمندوفر نقاباتی تأسس ایتدكدن صكره شرقده '''صلحی''' تأمین ایچون دینیه‌ستر نهری جنوبندن روس — رومن جبهه‌سنه قارشو حربه دوام ایتمك فكرندن فراغت ایتمك لازمكلیورایدی. بر قصقاچ تأثیری یاپه‌بیلمك ایچون بری قلاص غربندن یوقاره‌سه‌ره‌ت اوزرینه و دیكری دینیه‌ستر جنوبندن اولمق اوزره مضاعف بر تعرض اجراسی لازمكلیور ایده.|tr=Şarki Galiçya, ve Bukovinyada‌ şömendüfer nakabatı tesis itdikden soñra şarkda sulhı temin içün Dinyester nehri cenubundan rus-rumen cebhesine karşu harbe devam itmek fikrinden feragat itmek lazım geliyor idi. Bir kıskaç tesiri yapabilmek içün biri Kalas garbından yukara-yı Seret üzerine ve diğeri Dinyester cenubundan olmak üzere muzaaf bir taarruz icrası lazım geliyor idi.|metna-orîjînal=de:Unsere weitere Absicht, nach Herstellung der Eisenbahnverbindungen in Ostgalizien und in der Bukowina zur Erzwingung des Friedens im Osten den Feldzug gegen die russisch-rumänische Front südlich des Dnjestr fortzusetzen, mußte aufgegeben werden. Er sollte in einem doppelten Angriff über den oberen Sereth westlich Galatz und hart südlich des Dnjestr bestehen und auch zangenartig wirken.|t=Our further intention, the military campaign against the Russian-Romanian front to be continued south of the Dniestr to force '''peace''' in the east, after procurement of the railroad connections in Eastern Galicia and in the Bukovina, had to be abandoned. It was supposed to consist in a double attack over the upper Siret west of Galați as well as hard south of the Dniestr and go like pincers.}} #* {{jêder-kitêb/ceribandin|it |sernav=L'œuvre complète de Tchouang-tseu|terciman=Liou Kia-hway<f:Hant:劉家槐><f:Pinyin:Liú Jiāhuái>|orîjînal={{w|Zhuangzi (book)|Zhuang-zi}}|ji-aliyê=莊周<t:Zhuang Zhou>|zimanê-orîjînal=och|sala-orîjînal=c. 300 {{BCE}}|sal=1969|sal2=1982|sala-weşandinê2=2010|versiyona-nû=translated as|terciman2=Carlo Laurenti; Christine Leverd|sernav2=Zhuang-zi [Chuang-tzu]|serî2=Gli Adelphi|cildê-seriyê2=41|beş2=Libertà naturale|tercimeya-beşê2=Natural Freedom|çap2=6th|cih2=Milan|weşanxane2={{w|Adelphi Edizioni}}|rûpel2=17|isbn2=978-88-459-0950-4|metna-orîjînal=och:之人也,物莫之傷,大浸稽天而不溺,大旱、金石流、土山焦而不熱。|jêgirtin=Se le acque si alzassero fino al cielo, non annegherebbero. Se la siccità '''liquefacesse''' i metalli e infiammasse le montagne, non ne sarebbero neppure sfiorati.|t=If the waters were to rise to the skies, they wouldn't drown. If drought were to '''liquefy''' metals, and set fire to the mountains, they wouldn't even be touched by it.}} #* {{jêder-kitêb/ceribandin|fr |zimanê-termê=en|sal=1677|nivîskar=Guy Miege [''i.e.'', [[w:fr:Guy Miège|Guy Miège]]]|made=HAGGESS|urlya-madeyê=https://books.google.com/books?id=YbWtTwVbLioC&pg=PP857|sernav=A New Dictionary French and English, with Another English and French; According to the Present Use, and Modern Orthography of the French. Inrich’d with New Words, Choice Phrases, and Apposite Proverbs; Digested into a Most Accurate Method; and Contrived for the Use Both of English and Foreiners|cih=London|weşanxane=Printed by Tho[mas] Dawks, for Thomas Basset, at the George, near [[w:Clifford's Inn|Cliffords-inn]], in [[w:Fleet Street|Fleetstreet]]|stûn=3|oclc=874955677|jêgirtin='''HAGGESS''', '''haggas''', or '''haggis''', ſort d'appret fait avec des herbes, du larde caupé menu, des épices, des œufs, & du fromage, le tout mis enſemble & bouilli dans une panſe de brebis.|t='''HAGGESS''', '''haggas''', or '''haggis''', a sort of preparation made with herbs, bacon, spices, eggs, cheese, all put together and boiled in a sheep's belly.|brackets=1}} #*{{jêder-kovar/ceribandin|en|sernav=Art VIII.—Who are the True Conservatives?|kovar=The Quarterly Christian Spectator|cih=New Haven, Conn.|weşanxane=Published for the proprietor by Hezekiah Howe [...]|meh=November|sal=1838|cild=X|hejmar=IV|rûpel=608|urlya-rûpelê=https://books.google.com/books?id=S7MAAAAAMAAJ&pg=PA608|oclc=5090641|jêgirtin=They have guarded a few minds against '''sciolism''' and pretension in philosophy, and they have given a new and more imposing air to '''sciolism''' itself, and placed in the hands of the charlatan enchantments and magical arts, which hold fast its victims with a potency never granted to an inferior spell. There is no '''sciolism''' now which is more dangerous than that which is so very careful to inform us of its entire freedom from all that is superficial, {{...}}}} #*{{jêder-kovar/ceribandin|en |tarîx=March 8, 2023|paşnav=Wu|pêşnav=Huizhong|nivîskar2=Johnson Lai|sernav=Taiwan suspects Chinese ships cut islands’ internet cables|kovar={{w|The Washington Post}}|urlya-arşîvê=https://web.archive.org/web/20230311220100/https://www.washingtonpost.com/business/2023/03/08/matsu-taiwan-internet-cables-cut-china/d322fcae-bd70-11ed-9350-7c5fccd598ad_story.html|tarîxa-arşîvê=11 March 2023|issn=0190-8286|oclc=2269358|qisim=Business|tarîx2=April 18, 2023|sernav2=Chinese ships cut internet of Taiwan’s outlying islands|cure2=journal|kovar2={{w|AP News}}|urlya-arşîvê2=https://web.archive.org/web/20230308053048/https://apnews.com/article/matsu-taiwan-internet-cables-cut-china-65f10f5f73a346fa788436366d7a7c70|jêgirtin=Wang Chung Ming, the head of '''Lienchiang''' County, as the Matsu islands are officially called, said he and the legislator from Matsu went to Taipei shortly after the internet broke down to ask for help, and was told they would get priority in any future internet backup plans.{{...}}<br>Wang, the head of '''Lienchiang''' County, said he had mentioned the cables on a recent visit to China, where he had met an executive from China Mobile.}} #*{{jêder-kovar/ceribandin|en |sal=1994|meh=April|kovar=光華|tercimeya-kovar=Sinorama|url=https://books.google.com/books?id=GIRDAAAAYAAJ|cild=19|hejmar=1-6|weşanxane={{w|Government Information Office}}|issn=0256-9043|oclc=20549278|sal2=1994|meh2=April|2nivîskar=Yuan-ching Cheng|cure2=journal|sernav2=Kinmen- -From Battlefield to Tourist Destination|kovar2=台灣光華雜誌<t:Taiwan Panorama>|terciman2=Robert Taylor|urlya-arşîvê2=https://web.archive.org/web/20221021103917/https://www.taiwan-panorama.com/en/Articles/Details?Guid=c9335c12-7ec3-40e7-96be-33a81416a223|tarîxa-arşîvê2=21 October 2022|qisim2=Around Taiwan|jêgirtin=The temple to Colonel Li Kuang-chien lies near Hsiputou village in the northwest of Kinmen Island, around five kilometers from the town of '''Chincheng''' and next to the famous Hill 132.{{...}}<br>Because Kinmen was settled early, it bears many traces of the past. There are 21 listed historical monuments scattered all over the island. The only Grade I monument among them is the "Chiehhsiao Arch," commemorating Chiu Liang-kung's mother, located in East Houpu Street in the center of '''Chincheng'''.}} == {{ziman|ku}} == === Lêker === [[Wêne:Example.jpg|thumb|Mînak]] {{lêker|ku}} # [[testsdfsdafdasfas]] #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Mîr Kamûran di sala 1947 an de li '''Fransayê''' bicîh bû, û heta 1970ê li Xwendegeha zimanên rojhilatî ya Zaningeha Parîsê Profesorîya ziman û çanda Kurdî kir.|nivîskar=Kamuran A.Bedir Khan|nivîskar2=Josefe Bertolino|nivîskar3=Kendal Nezan|sernav=Institut Kurde De Paris Ferhenga Kurdî-Fransizî|cih=Istanbul|weşanxane={{w|Weşanên Avesta|Avesta}}|sal=2017|çap=1|rûpel=XXXIX|isbn=978-2-360134311}} #* {{jêder-malper/ceribandin|ku|jêgirtin=Li gor amareke ajanseke selametiya xorakê, xelkê '''Fransa''' salane nêzîkî 4,000 ton Beqan dixwin, ku ew jî bi qasî goştê 40 nehengên şîn e. Ji bo dabînkirina pêwîstiya bazarê jî, li '''Fransayê''' kêlgeh û cihên taybet ên xwedîkirina Beqan hene. Beşek ji wêneyên di vê wênenûçeyê de, kêlgeha xwedîkirina Beqan li navçeyeke '''Fransayê''' ye, herwiha beşek ji wêneyan jî awayê patin û çêkirina xwarinê ji goştê Beqan di xwaringehan de û pêşkêşkirina wan bi mêvanan nîşan didin.|nivîskar=ciwan ikbal|sernav=Fransî di salekê de 4,000 ton Beqan dixwin|url=https://www.rudaw.net/kurmanci/world/101020201|dîrok=10.10.2020}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Bale awayeke dansê ye. Babetek ji şanoya musîkal e. Ji bo dansa baleyê, divê meriv cansivik û nerm be. Loma jî bale gelekî antremanê dixwaze û divê meriv di temenekî biçûk de dest pê bike. Di baleyê de, meriv li gor livên diyarkirî dansê dike. Hinek ji wan tu di wêne de dibînî. Ji ber ku bale li '''Fransayê''' bi pêş ketiye, loma navên livên wê jî bi [[fransî]] ne.|amadekar=Komxebat|sernav=Ansîklopediya Zarokan|cih=Denmark<br>|weşanxane={{w|Weşanên APECl|APEC}}|sal=2004|rûpel=25|isbn=91-89675-32-0}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala-weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani|weşanxane=BRILL|ISBN=9789004754430|rûpel=383}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala-weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji %&nbsp;70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala-weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}} #* {{jêder-malper/ceribandin|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|tarîx=2021-05-04}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşandxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}} #* {{jêder-malper/ceribandin|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|sal=2025|tarîx=2025-02-02|jêgirtin=adfasdf}} #* {{jêder-kitêb/ceribandin|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}} # Maneya 3an. ej0zrgw3znt160qiw8b7tmsfdxmefje 6291563 6291562 2026-07-26T14:09:14Z Wikihez 7344 6291563 wikitext text/x-wiki {{/vê rêzê neguhêre}} == {{ziman|en}} == === Lêker === [[Wêne:Example.jpg|thumb|Mînak]] {{lêker|en}} # Maneya 1ê. #: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}} #* {{jêder-kovar/ceribandin|en|nivîskar=Justin Chang|lînka-nivîskar=Justin Chang|sernav=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|urlya-arşîvê=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|tarîxa-arşîvê=28 June 2017|kovar=[[w:Variety (magazine)|Variety]]|tarîx=25 May 2011|issn=0042-2738|oclc=960911146|jêgirtin=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}} #* {{jêder-kitêb/ceribandin|en|nivîskar=F[rancis] Scott Fitzgerald|lînka-nivîskar=F. Scott Fitzgerald|beş=IX|sernav=[[w:The Great Gatsby|The Great Gatsby]]|cih=New York, N.Y.|weşanxane=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|sal=1925|oclc=884653065|cih2=New York, N.Y.|weşanxane2=Charles Scribner’s Sons|sal2=1953|isbn2=978-0-684-16498-4|rûpel2=182|urlya-rûpelê2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|jêgirtin=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}} #* {{jêder-malper/ceribandin|en|nivîskar=Meg Downey|sernav=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|urlya-arşîvê=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|tarîxa-arşîvê=24 June 2018|xebat=[[w:Polygon (website)|Polygon]]|tarîx=7 May 2018|jêgirtin=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}} #* {{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=The Republic of Molossia has played a considerable role in developing '''intermicronational''' diplomacy.}} #* {{jêder-kitêb/ceribandin|en |sal=c. 1988|nivîskar=Peter W. Rodino, Jr.; et al.|sernav=Report of the Congressional Committees Investigating the Iran/Contra Affair|urlya-rûpelê=http://books.google.com/books?id=ew_K3auTwEgC&pg=PA646&dq=phone+call|rûpel=646|jêgirtin=On November 20, they had a '''phone call''' and later a meeting with others lasting at least 1 1/2 hours.}} #* {{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=Centrally acting '''sympathicolytics''' cause a decrease in the sympathetic outflow from the brainstem. This reduction in sympathetic outflow brings about vasodilatation of the peripheral vascular bed with a concomitant drop in the blood pressure}} #* {{jêder-kitêb/ceribandin|en |sal=2005|edîtor=Martin Endress; George Psathas; Hisashi Nasu|beş=Between the Everyday Life-World and the World of Social Scientific Theory—Towards an “Adequate” Social Thory|nivîskar=Hisashi Nasu|sernav=Explorations of the Life-World: Continuing Dialogues with Alfred Schutz|weşanxane=Springer|isbn=1402032196|rûpel=136|jêgirtin=It is not only the '''man on the street''' who is characterized by taking one's own system of relevance for granted. This holds true for the expert,....}} #* {{jêder-kitêb/ceribandin|en |sal=2002|nivîskar=Peter Shirlow|beş=Sinn Féin: Beyond; within Containment|hejmara-beşê=4|edîtor=Stefan Wolff; Jörg Neuheiser|sernav=Peace at Last?: The Impact of the Good Friday Agreement on Northern Ireland|weşanxane=Berghahn Books|isbn=157181518X|urlya-rûpelê=http://books.google.com/books?id=QyInASKCFjcC&pg=PA62&dq=ethnosectarian&sig=Q54T1Vz3vsKnqd05666N1ddFki8|rûpel=62|jêgirtin=Moreover, language, discourse, and dialogue, their definition and mobilisation, create chains of equivalence which enclose subjects within not only '''ethnosectarian''' but at times entirely self-referential and self-sustained notions of identity, practice and collective devotion.}} #* {{jêder-kitêb/ceribandin|en |sernav=Extreme Hydrological Events: New Concepts for Security|edîtor=O.F. Vasiliev; P.H.A.J.M. van Gelder; E.J. Plate; M.V. Bolgov|sal=2007|weşanxane=[[w:Springer Science+Business Media|Springer]]|isbn=1402057393|beş=Predetermination of Floods|nivîskar=Pierre Hubert; Ioulia Tchiguirinskaia; Daniel Schertzer; Hocine Bendjoudi; Shaun Lovejoy|qisim=On Probability and Return Periods of Floods|hejmara-qismê=2|rûpel=187|jêgirtin=It is perfectly legitimate to be interested, particularly for safety studies, by millennium floods or floods or '''decamillennium''' floods (i.e. having a probability 0,001 and 0,0001 respectively to occur in the course of given year) of a river which did not exist five thousand years ago and that will not exist perhaps any more in ten thousand years, similarly to the industrial interest in the probability of faults which will not undoubtedly occur, because the minimization of their probability is an aspect of a policy of industrial security.}} #* {{jêder-kitêb/ceribandin|en |sal=1995|nivîskar=w:Robert M. Solovay|nivîskarê-sereke=w:Kurt Gödel|edîtor=w:Solomon Feferman; w:John W. Dawson Jr.; w:Warren Goldfarb; {{w|Charles Parsons (philosopher)|Charles Parsons}}; w:Robert M. Solovay|beş=Notes to *1970 &#91;{{w|Godel's ontological proof}}&#93;|sernav=Kurt Gödel: Collected Works: Volume III|urlya-rûpelê=https://books.google.com.au/books?id=gDzbuUwma5MC&pg=PA419&dq=%22Hausdorff+gap%22%7C%22Hausdorff+gaps%22&hl=en&newbks=1&newbks_redir=0&sa=X&ved=2ahUKEwjo-o7D9OT7AhVQJUQIHaSlBIgQ6AF6BAhXEAI#v=onepage&q=%22Hausdorff%20gap%22%7C%22Hausdorff%20gaps%22&f=false|rûpel=419|weşanxane=w:Oxford University Press|jêgirtin=Of course, Hausdorff did not talk of models of set theory or prove absoluteness results. He gave a direct construction in ''ZFC'' of what is now called a '''Hausdorff gap''' and proved properties 1 through 4 for his construction. The notion of a '''Hausdorff gap''' and the proof that '''Hausdorff gaps''' are indestructible under cardinal preserving extensions (property 5 above) are due to Kunen (unpublished).}} #* {{jêder-kitêb/ceribandin|en |sal=1925|nivîskar={{w|F. Scott Fitzgerald|F[rancis] Scott Fitzgerald}}|beş=IX|sernav=w:The Great Gatsby|cih=New York, N.Y.|weşanxane=w:Charles Scribner's Sons|oclc=884653065|sal2=1953|cih2=New York, N.Y.|weşanxane2=Charles Scribner’s Sons|isbn2=978-0-684-16498-4|rûpel2=182|urlya-rûpelê2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|jêgirtin=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.|lccn=gm 95-1}} #* {{jêder-kitêb/ceribandin|en |nivîskar=w:John Boyle O'Reilly|beş=On the Trail|sernav=[[w:Moondyne|Moondyne: A Story from the Under-world]]|cih=London|weşanxane=[[w:Routledge|George Routledge and Sons]]|sal=1878|sala-weşandinê=1879|oclc=39983928|sernav2=Moondyne: A Story of Convict Life in Australia|cih2=London|weşanxane2=George Routledge & Sons, Limited, Broadway House, {{w|Ludgate Hill}}|sal2=[1880s]|qisim2=book first|pages2=23–24|urlya-rûpelê2=https://archive.org/stream/moondynestory00oreirich#page/24/mode/1up/|oclc2=83033698|jêgirtin=It was sore travelling for horse and man under the blazing sun, with no food nor water save what he pressed from the pith of the palms, and even these were growing scarce. The only life on the plains was the hard and dusty scrub. Every hour brought a more hopeless and '''grislier''' desolation.}} #* {{jêder-kitêb/ceribandin|en |sal=1966|sala-orîjînal=1858|nivîskar=w:Hans Christian Andersen|terciman=Pat Shaw Iversen|orîjînal=Suppe paa en Pølsepind|zimanê-orîjînal=da|beş=Soup from a Sausage Peg|sernav=The Snow Queen and Other Tales|serî=Signet Classic|cildê-seriyê=CT334|cih=New York, N.Y.|weşanxane=w:New American Library|rûpel=224|urlya-rûpelê=http://books.google.com/books?id=2tjWAAAAMAAJ&q=%22from+your+own+nook+and+cranny%22#search_anchor|oclc=636818779|metna-orîjînal=da:Det er underligt at komme hjemme fra sin egen '''Krinkelkrog''', gaae med Skib, der ogsaa er en Slags '''Krinkelkrog''', og saa pludselig være over hundrede Mile borte og staae i et fremmedt Land.|jêgirtin=It’s strange to come away from home, from your own '''nook and cranny''', to go by ship – which is also a kind of '''nook and cranny''' – and then suddenly be more than a hundred miles away and stand in a foreign land!}} #* {{jêder-kitêb/ceribandin|en |sal=2013|nivîskar=Bernd Klauer; Reiner Manstetten; Thomas Petersen; Johannes Schiller|zimanê-eserê=de|beş=Materielle Bestände|sernav=Die Kunst, langfristig zu denken : Wege zur Nachhaltigkeit<t:The art of thinking long-term : paths to sustainability>|cih=Baden-Baden|weşanxane=Nomos|versiyona-nû=translated as|terciman2=Kathleen Cross|sal2=2016|sernav2=Sustainability and the Art of Long-Term Thinking|beş2=Material stocks|cih2=London; New York|weşanxane2=Routledge, Taylor & Francis Group|qisim2=4.3.1. Material sets and stocks|rûpel2=44|isbn2=9781134986187|jêgirtin=The property of '''appertainment''' of a set is therefore the property which, by definition, has to be given in every element of the set.}} #* {{jêder-kitêb/ceribandin|en |nivîskar={{w|Andrew Seth Pringle-Pattison|Andrew Seth}}|sernav=Hegelianism and Personality|çap=2nd|sal=1887|sala-weşandinê=1893|rûpel=216|rêz-sade=footnote|cih=Edinburgh; London|weşanxane=William Blackwood and Sons|versiyona-nû=quoted in|sal2=1893|2nivîskar=w:David George Ritchie|sernav2=Darwin and Hegel, with other Philosophical Studies|cih2=London|weşanxane2=S. Sonnenschein & Co.; New York, N.Y.: [[w:Macmillan Publishers (United States)|Macmillan & Co.]]|rûpel2=72|urlya-rûpelê2=http://books.google.com/books?id=8yGEAHvAwiIC&q=%22An+absolute+system+cannot+afford+to+leave+any+nook+or+cranny+of+existence+unexplored%22#search_anchor|rêz-sade2=note 1|oclc2=3299658|jêgirtin=An absolute system cannot afford to leave '''any nook or cranny''' of existence unexplored.}} #* {{jêder-kitêb/ceribandin|en |nivîskar=w:David George Ritchie|jê-tê-girtin=w:Andrew Seth Pringle-Pattison|sernav=Hegelianism and Personality|çap=2nd|kesên-din=1893, page 216, footnote|cihê-jêgirtinê=Darwin and Hegel, with other Philosophical Studies|cih=London|weşanxane=S. Sonnenschein & Co.; New York, N.Y.: [[w:Macmillan Publishers (United States)|Macmillan & Co.]]|sal=1893|rûpel=72|rêz-sade=note 1|urlya-rûpelê=http://books.google.com/books?id=8yGEAHvAwiIC&q=%22An+absolute+system+cannot+afford+to+leave+any+nook+or+cranny+of+existence+unexplored%22#search_anchor|oclc=3299658|jêgirtin=An absolute system cannot afford to leave '''any nook or cranny''' of existence unexplored.}} #* {{jêder-kovar/ceribandin|en |nivîskar=w:Hayden Carruth|sernav=Making It New|kovar=w:The Hudson Review|cih=New York, N.Y.|weşanxane=Hudson Review, Inc.|meh=Summer|sal=1968|cild=XXI|hejmar=2|issn=2325-5935|oclc=920393805|versiyona-nû=reprinted as|cure2=book|beş2=From ‘Making It New’ [''Body Rags'']|edîtor2=Howard Nelson|sernav2=On the Poetry of Galway Kinnell: The Wages of Dying|serî2=Under Discussion|cih2=Ann Arbor, Mich.|weşanxane2={{w|University of Michigan Press}}|sal2=1987|rûpel2=75|urlya-rûpelê2=https://books.google.com/books?id=7cXV26PmJUYC&pg=PA75|isbn2=978-0-472-09376-2|jêgirtin=In his &#91;{{w|Galway Kinnell}}'s&#93; new book, ''Body Rags'', he has brought this style to a kind of perfection, especially in two poems about the killing of animals, "The Porcupine" and "The Bear." These are the '''grisliest''' poems I have ever read.}} #* {{jêder-kitêb/ceribandin|fr |sal=1973|nivîskar=w:Claude Simon|sernav=Tryptique|weşanxane=Éditions de Minuit|rûpel=12|jêgirtin=Les sons de la cloche '''égrenant''' les quarts, les demies et les heures {{...}}|t=The sounds of the clock '''rattling off''' the quarters, the halves and the hours {{...}}}} #* {{jêder-kitêb/ceribandin|de |sal=1999|nivîskar=Paul Hoffmann|hevnivîskar=Stefan H. Brandenburger; Ulrike Brauner; Thomas Hieke|sernav=Q 22:28, 30: You Will Judge the Twelve Tribes of Israel|serî=Documenta Q: Reconstructions of Q Through Two Centuries of Gospel Research Excerpted, Sorted and Evaluated|cildê-seriyê=volume 5|isbn=90-429-0734-7|rûpel=189|jêgirtin=Zumal in beiden Fällen eine Partizipialform vorliegt, liegt es nahe, die '''Zeitworte''' als Varianten anzusehen.}} #* {{jêder-kitêb/ceribandin|ru |nivîskar=lw:ru:[[Филарет (Дроздов)|Митрополи́т Филаре́т]]<tr:-><t:Mitropolit Filaret>|beş=ru:Сло́во предъ приведеніемъ къ присягѣ Санктпетербурксаго дворя́нства и гражданъ для избранія суде́й|tarîx=1818-02-01<!--Converted to Gregorian-->|sala-weşandinê=1873|cih=Moscow|sernav=ru:Сочинеиія Филарета Митрополита Московскаго и Коломенскаго|cild=I|rûpel=253|urlya-rûpelê=https://books.google.com/books?id=yWEcAAAAMAAJ&pg=PA253|jêgirtin=[[изъ|Изъ]] [[сей|сего́]] [[ви́дно]], [[что]] [[судья|судія́]], [[как|какъ]] [[блюсти́тель]] [[общественный|обще́ственнаго]] [[порядок|поря́дка]], [[должен|долженъ]] '''объима́ть''' [[и]] [[проника́ть]] [[мысль|мы́слію]] [[весь]] [[состав|соста́въ]] [[общество|о́бщества]];|t=From this it becomes discernible that a judge, in his function as a warden of public order, should '''enclose''' and traverse the whole body of the society;}} #* {{jêder-kitêb/ceribandin|yi |nivîskar=I.J. Singer|sernav=Fun a velt vos iz nishto mer|sal=1946|beş=yi:אַ ייד אַ מלמד פֿאַרגלוסט זיך צו ווערן אַ מלאך אום פּורים און פֿאַרלירט, נעבעך, אַן אויג<subst:ייד/ייִד,מלמד/מעלאַמעד,פּורים/פּורעם,מלאך/מאַלעך><t:A Jew, a teacher, decides to become an angel on Purim and, unfortunately, loses an eye>|jêgirtin=יעדן '''זמן''' אויפֿסניי, חול־המועד סוכות אָדער חול־המועד פּסח, בין איך געווען זייער האָפֿערדיק, וואָס מען האָט מיך גענומען איין מלמד און אָנגעדונגען צו אַן אַנדערן, גלײבנדיק, אַז דער נייער וועט זיין בעסער ווי דער אַלטער.|subst=זמן//זמאַן,חול־המועד//כאַלעמויעד,סוכות//סוקוס,פּסח//פּייסעך,מלמד//מעלאַמעד,נייער//נײַער|tercime=Every '''term''' anew, during the intermediary periods of Sukkot and Pesach, I was very confident in being taken from one melamed and given to another, believing the new one would be better than the old.}} #* {{jêder-kitêb/ceribandin|ru |sal=1983|nivîskar=w:Samuil Marshak|sernav=Баллада о королевском бутерброде<subst:бутер/бутэр><t:Ballad of the King's Bread>|orîjînal=The King's Breakfast|ji-aliyê=w:A. A. Milne|metna-orîjînal=en:"Nobody," / He whimpered, / "Could call me / A fussy man; / I only want / A little bit / Of butter for / My bread!"|zimanê-orîjînal=en|jêgirtin=— [[никто|Никто́]] [[не]] [[сказать|ска́жет]], [[бу́дто]] [[я]] / [[тиран|Тира́н]] [[и]] [[сумасбро́д]], / [[за|За]] [[то́]], [[что]] [[к]] [[чай|ча́ю]] [[я]] [[любить|люблю́]] / [[хороший|Хоро́ший]] '''бутербро́д'''.|subst=бутербро́д/бутэрбро́д|t="No one would say that I / Am a tyrant and madman, / For the fact that with my tea I like / A good '''bread-and-butter sandwich'''."}} #* {{jêder-kitêb/ceribandin|ota |sal=1922|sernav=ota:ادارهٔ حرب و سیاست<tr:Idare-i harb ve siyaset>|cih=Istanbul|weşanxane=ota:مطبعهٔ عثمانیه|rûpel=176|urlya-rûpelê=https://babel.hathitrust.org/cgi/pt?id=uc1.aa0006749931&view=1up&seq=182|versiyona-nû=translation of|ziman2=de|sal2=1922|2nivîskar=w:Erich Ludendorff|sernav2=Kriegsführung und Politik|cih2=Berlin|weşanxane2=E. S. Mittler & Sohn|rûpel2=199|urlya-rûpelê2=https://archive.org/details/kriegfhrungund00lude/page/199|jêgirtin=شرقی غالیچیا، و بوقوونیاده شمندوفر نقاباتی تأسس ایتدكدن صكره شرقده '''صلحی''' تأمین ایچون دینیه‌ستر نهری جنوبندن روس — رومن جبهه‌سنه قارشو حربه دوام ایتمك فكرندن فراغت ایتمك لازمكلیورایدی. بر قصقاچ تأثیری یاپه‌بیلمك ایچون بری قلاص غربندن یوقاره‌سه‌ره‌ت اوزرینه و دیكری دینیه‌ستر جنوبندن اولمق اوزره مضاعف بر تعرض اجراسی لازمكلیور ایده.|tr=Şarki Galiçya, ve Bukovinyada‌ şömendüfer nakabatı tesis itdikden soñra şarkda sulhı temin içün Dinyester nehri cenubundan rus-rumen cebhesine karşu harbe devam itmek fikrinden feragat itmek lazım geliyor idi. Bir kıskaç tesiri yapabilmek içün biri Kalas garbından yukara-yı Seret üzerine ve diğeri Dinyester cenubundan olmak üzere muzaaf bir taarruz icrası lazım geliyor idi.|metna-orîjînal=de:Unsere weitere Absicht, nach Herstellung der Eisenbahnverbindungen in Ostgalizien und in der Bukowina zur Erzwingung des Friedens im Osten den Feldzug gegen die russisch-rumänische Front südlich des Dnjestr fortzusetzen, mußte aufgegeben werden. Er sollte in einem doppelten Angriff über den oberen Sereth westlich Galatz und hart südlich des Dnjestr bestehen und auch zangenartig wirken.|t=Our further intention, the military campaign against the Russian-Romanian front to be continued south of the Dniestr to force '''peace''' in the east, after procurement of the railroad connections in Eastern Galicia and in the Bukovina, had to be abandoned. It was supposed to consist in a double attack over the upper Siret west of Galați as well as hard south of the Dniestr and go like pincers.}} #* {{jêder-kitêb/ceribandin|it |sernav=L'œuvre complète de Tchouang-tseu|terciman=Liou Kia-hway<f:Hant:劉家槐><f:Pinyin:Liú Jiāhuái>|orîjînal={{w|Zhuangzi (book)|Zhuang-zi}}|ji-aliyê=莊周<t:Zhuang Zhou>|zimanê-orîjînal=och|sala-orîjînal=c. 300 {{BCE}}|sal=1969|sal2=1982|sala-weşandinê2=2010|versiyona-nû=translated as|terciman2=Carlo Laurenti; Christine Leverd|sernav2=Zhuang-zi [Chuang-tzu]|serî2=Gli Adelphi|cildê-seriyê2=41|beş2=Libertà naturale|tercimeya-beşê2=Natural Freedom|çap2=6th|cih2=Milan|weşanxane2={{w|Adelphi Edizioni}}|rûpel2=17|isbn2=978-88-459-0950-4|metna-orîjînal=och:之人也,物莫之傷,大浸稽天而不溺,大旱、金石流、土山焦而不熱。|jêgirtin=Se le acque si alzassero fino al cielo, non annegherebbero. Se la siccità '''liquefacesse''' i metalli e infiammasse le montagne, non ne sarebbero neppure sfiorati.|t=If the waters were to rise to the skies, they wouldn't drown. If drought were to '''liquefy''' metals, and set fire to the mountains, they wouldn't even be touched by it.}} #* {{jêder-kitêb/ceribandin|fr |zimanê-termê=en|sal=1677|nivîskar=Guy Miege [''i.e.'', [[w:fr:Guy Miège|Guy Miège]]]|made=HAGGESS|urlya-madeyê=https://books.google.com/books?id=YbWtTwVbLioC&pg=PP857|sernav=A New Dictionary French and English, with Another English and French; According to the Present Use, and Modern Orthography of the French. Inrich’d with New Words, Choice Phrases, and Apposite Proverbs; Digested into a Most Accurate Method; and Contrived for the Use Both of English and Foreiners|cih=London|weşanxane=Printed by Tho[mas] Dawks, for Thomas Basset, at the George, near [[w:Clifford's Inn|Cliffords-inn]], in [[w:Fleet Street|Fleetstreet]]|stûn=3|oclc=874955677|jêgirtin='''HAGGESS''', '''haggas''', or '''haggis''', ſort d'appret fait avec des herbes, du larde caupé menu, des épices, des œufs, & du fromage, le tout mis enſemble & bouilli dans une panſe de brebis.|t='''HAGGESS''', '''haggas''', or '''haggis''', a sort of preparation made with herbs, bacon, spices, eggs, cheese, all put together and boiled in a sheep's belly.|brackets=1}} #*{{jêder-kovar/ceribandin|en|sernav=Art VIII.—Who are the True Conservatives?|kovar=The Quarterly Christian Spectator|cih=New Haven, Conn.|weşanxane=Published for the proprietor by Hezekiah Howe [...]|meh=November|sal=1838|cild=X|hejmar=IV|rûpel=608|urlya-rûpelê=https://books.google.com/books?id=S7MAAAAAMAAJ&pg=PA608|oclc=5090641|jêgirtin=They have guarded a few minds against '''sciolism''' and pretension in philosophy, and they have given a new and more imposing air to '''sciolism''' itself, and placed in the hands of the charlatan enchantments and magical arts, which hold fast its victims with a potency never granted to an inferior spell. There is no '''sciolism''' now which is more dangerous than that which is so very careful to inform us of its entire freedom from all that is superficial, {{...}}}} #*{{jêder-kovar/ceribandin|en |tarîx=March 8, 2023|paşnav=Wu|pêşnav=Huizhong|nivîskar2=Johnson Lai|sernav=Taiwan suspects Chinese ships cut islands’ internet cables|kovar={{w|The Washington Post}}|urlya-arşîvê=https://web.archive.org/web/20230311220100/https://www.washingtonpost.com/business/2023/03/08/matsu-taiwan-internet-cables-cut-china/d322fcae-bd70-11ed-9350-7c5fccd598ad_story.html|tarîxa-arşîvê=11 March 2023|issn=0190-8286|oclc=2269358|qisim=Business|tarîx2=April 18, 2023|sernav2=Chinese ships cut internet of Taiwan’s outlying islands|cure2=journal|kovar2={{w|AP News}}|urlya-arşîvê2=https://web.archive.org/web/20230308053048/https://apnews.com/article/matsu-taiwan-internet-cables-cut-china-65f10f5f73a346fa788436366d7a7c70|jêgirtin=Wang Chung Ming, the head of '''Lienchiang''' County, as the Matsu islands are officially called, said he and the legislator from Matsu went to Taipei shortly after the internet broke down to ask for help, and was told they would get priority in any future internet backup plans.{{...}}<br>Wang, the head of '''Lienchiang''' County, said he had mentioned the cables on a recent visit to China, where he had met an executive from China Mobile.}} #*{{jêder-kovar/ceribandin|en |sal=1994|meh=April|kovar=光華|tercimeya-kovar=Sinorama|url=https://books.google.com/books?id=GIRDAAAAYAAJ|cild=19|hejmar=1-6|weşanxane={{w|Government Information Office}}|issn=0256-9043|oclc=20549278|sal2=1994|meh2=April|2nivîskar=Yuan-ching Cheng|cure2=journal|sernav2=Kinmen- -From Battlefield to Tourist Destination|kovar2=台灣光華雜誌<t:Taiwan Panorama>|terciman2=Robert Taylor|urlya-arşîvê2=https://web.archive.org/web/20221021103917/https://www.taiwan-panorama.com/en/Articles/Details?Guid=c9335c12-7ec3-40e7-96be-33a81416a223|tarîxa-arşîvê2=21 October 2022|qisim2=Around Taiwan|jêgirtin=The temple to Colonel Li Kuang-chien lies near Hsiputou village in the northwest of Kinmen Island, around five kilometers from the town of '''Chincheng''' and next to the famous Hill 132.{{...}}<br>Because Kinmen was settled early, it bears many traces of the past. There are 21 listed historical monuments scattered all over the island. The only Grade I monument among them is the "Chiehhsiao Arch," commemorating Chiu Liang-kung's mother, located in East Houpu Street in the center of '''Chincheng'''.}} == {{ziman|ku}} == === Lêker === [[Wêne:Example.jpg|thumb|Mînak]] {{lêker|ku}} # [[testsdfsdafdasfas]] #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Mîr Kamûran di sala 1947 an de li '''Fransayê''' bicîh bû, û heta 1970ê li Xwendegeha zimanên rojhilatî ya Zaningeha Parîsê Profesorîya ziman û çanda Kurdî kir.|nivîskar=Kamuran A.Bedir Khan|nivîskar2=Josefe Bertolino|nivîskar3=Kendal Nezan|sernav=Institut Kurde De Paris Ferhenga Kurdî-Fransizî|cih=Istanbul|weşanxane={{w|Weşanên Avesta|Avesta}}|sal=2017|çap=1|rûpel=XXXIX|isbn=978-2-360134311}} #* {{jêder-malper/ceribandin|ku|jêgirtin=Li gor amareke ajanseke selametiya xorakê, xelkê '''Fransa''' salane nêzîkî 4,000 ton Beqan dixwin, ku ew jî bi qasî goştê 40 nehengên şîn e. Ji bo dabînkirina pêwîstiya bazarê jî, li '''Fransayê''' kêlgeh û cihên taybet ên xwedîkirina Beqan hene. Beşek ji wêneyên di vê wênenûçeyê de, kêlgeha xwedîkirina Beqan li navçeyeke '''Fransayê''' ye, herwiha beşek ji wêneyan jî awayê patin û çêkirina xwarinê ji goştê Beqan di xwaringehan de û pêşkêşkirina wan bi mêvanan nîşan didin.|nivîskar=ciwan ikbal|sernav=Fransî di salekê de 4,000 ton Beqan dixwin|url=https://www.rudaw.net/kurmanci/world/101020201|dîrok=10.10.2020}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Bale awayeke dansê ye. Babetek ji şanoya musîkal e. Ji bo dansa baleyê, divê meriv cansivik û nerm be. Loma jî bale gelekî antremanê dixwaze û divê meriv di temenekî biçûk de dest pê bike. Di baleyê de, meriv li gor livên diyarkirî dansê dike. Hinek ji wan tu di wêne de dibînî. Ji ber ku bale li '''Fransayê''' bi pêş ketiye, loma navên livên wê jî bi [[fransî]] ne.|amadekar=Komxebat|sernav=Ansîklopediya Zarokan|cih=Denmark<br>|weşanxane={{w|Weşanên APECl|APEC}}|sal=2004|rûpel=25|isbn=91-89675-32-0}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala-weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani|weşanxane=BRILL|ISBN=9789004754430|rûpel=383}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala-weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji %&nbsp;70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala-weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}} #* {{jêder-malper/ceribandin|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|tarîx=2021-05-04}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|dîrok=2013|weşanxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}} #* {{jêder-malper/ceribandin|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|tarîx=2025-02-02|jêgirtin=adfasdf}} #* {{jêder-kitêb/ceribandin|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}} # Maneya 3an. 5e1ib7plxmapch8kgbr1t0of6e2255n 6291564 6291563 2026-07-26T14:09:50Z Wikihez 7344 6291564 wikitext text/x-wiki {{/vê rêzê neguhêre}} == {{ziman|en}} == === Lêker === [[Wêne:Example.jpg|thumb|Mînak]] {{lêker|en}} # Maneya 1ê. #: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}} #* {{jêder-kovar/ceribandin|en|nivîskar=Justin Chang|lînka-nivîskar=Justin Chang|sernav=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|urlya-arşîvê=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|tarîxa-arşîvê=28 June 2017|kovar=[[w:Variety (magazine)|Variety]]|tarîx=25 May 2011|issn=0042-2738|oclc=960911146|jêgirtin=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}} #* {{jêder-kitêb/ceribandin|en|nivîskar=F[rancis] Scott Fitzgerald|lînka-nivîskar=F. Scott Fitzgerald|beş=IX|sernav=[[w:The Great Gatsby|The Great Gatsby]]|cih=New York, N.Y.|weşanxane=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|sal=1925|oclc=884653065|cih2=New York, N.Y.|weşanxane2=Charles Scribner’s Sons|sal2=1953|isbn2=978-0-684-16498-4|rûpel2=182|urlya-rûpelê2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|jêgirtin=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}} #* {{jêder-malper/ceribandin|en|nivîskar=Meg Downey|sernav=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|urlya-arşîvê=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|tarîxa-arşîvê=24 June 2018|xebat=[[w:Polygon (website)|Polygon]]|tarîx=7 May 2018|jêgirtin=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}} #* {{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=The Republic of Molossia has played a considerable role in developing '''intermicronational''' diplomacy.}} #* {{jêder-kitêb/ceribandin|en |sal=c. 1988|nivîskar=Peter W. Rodino, Jr.; et al.|sernav=Report of the Congressional Committees Investigating the Iran/Contra Affair|urlya-rûpelê=http://books.google.com/books?id=ew_K3auTwEgC&pg=PA646&dq=phone+call|rûpel=646|jêgirtin=On November 20, they had a '''phone call''' and later a meeting with others lasting at least 1 1/2 hours.}} #* {{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=Centrally acting '''sympathicolytics''' cause a decrease in the sympathetic outflow from the brainstem. This reduction in sympathetic outflow brings about vasodilatation of the peripheral vascular bed with a concomitant drop in the blood pressure}} #* {{jêder-kitêb/ceribandin|en |sal=2005|edîtor=Martin Endress; George Psathas; Hisashi Nasu|beş=Between the Everyday Life-World and the World of Social Scientific Theory—Towards an “Adequate” Social Thory|nivîskar=Hisashi Nasu|sernav=Explorations of the Life-World: Continuing Dialogues with Alfred Schutz|weşanxane=Springer|isbn=1402032196|rûpel=136|jêgirtin=It is not only the '''man on the street''' who is characterized by taking one's own system of relevance for granted. This holds true for the expert,....}} #* {{jêder-kitêb/ceribandin|en |sal=2002|nivîskar=Peter Shirlow|beş=Sinn Féin: Beyond; within Containment|hejmara-beşê=4|edîtor=Stefan Wolff; Jörg Neuheiser|sernav=Peace at Last?: The Impact of the Good Friday Agreement on Northern Ireland|weşanxane=Berghahn Books|isbn=157181518X|urlya-rûpelê=http://books.google.com/books?id=QyInASKCFjcC&pg=PA62&dq=ethnosectarian&sig=Q54T1Vz3vsKnqd05666N1ddFki8|rûpel=62|jêgirtin=Moreover, language, discourse, and dialogue, their definition and mobilisation, create chains of equivalence which enclose subjects within not only '''ethnosectarian''' but at times entirely self-referential and self-sustained notions of identity, practice and collective devotion.}} #* {{jêder-kitêb/ceribandin|en |sernav=Extreme Hydrological Events: New Concepts for Security|edîtor=O.F. Vasiliev; P.H.A.J.M. van Gelder; E.J. Plate; M.V. Bolgov|sal=2007|weşanxane=[[w:Springer Science+Business Media|Springer]]|isbn=1402057393|beş=Predetermination of Floods|nivîskar=Pierre Hubert; Ioulia Tchiguirinskaia; Daniel Schertzer; Hocine Bendjoudi; Shaun Lovejoy|qisim=On Probability and Return Periods of Floods|hejmara-qismê=2|rûpel=187|jêgirtin=It is perfectly legitimate to be interested, particularly for safety studies, by millennium floods or floods or '''decamillennium''' floods (i.e. having a probability 0,001 and 0,0001 respectively to occur in the course of given year) of a river which did not exist five thousand years ago and that will not exist perhaps any more in ten thousand years, similarly to the industrial interest in the probability of faults which will not undoubtedly occur, because the minimization of their probability is an aspect of a policy of industrial security.}} #* {{jêder-kitêb/ceribandin|en |sal=1995|nivîskar=w:Robert M. Solovay|nivîskarê-sereke=w:Kurt Gödel|edîtor=w:Solomon Feferman; w:John W. Dawson Jr.; w:Warren Goldfarb; {{w|Charles Parsons (philosopher)|Charles Parsons}}; w:Robert M. Solovay|beş=Notes to *1970 &#91;{{w|Godel's ontological proof}}&#93;|sernav=Kurt Gödel: Collected Works: Volume III|urlya-rûpelê=https://books.google.com.au/books?id=gDzbuUwma5MC&pg=PA419&dq=%22Hausdorff+gap%22%7C%22Hausdorff+gaps%22&hl=en&newbks=1&newbks_redir=0&sa=X&ved=2ahUKEwjo-o7D9OT7AhVQJUQIHaSlBIgQ6AF6BAhXEAI#v=onepage&q=%22Hausdorff%20gap%22%7C%22Hausdorff%20gaps%22&f=false|rûpel=419|weşanxane=w:Oxford University Press|jêgirtin=Of course, Hausdorff did not talk of models of set theory or prove absoluteness results. He gave a direct construction in ''ZFC'' of what is now called a '''Hausdorff gap''' and proved properties 1 through 4 for his construction. The notion of a '''Hausdorff gap''' and the proof that '''Hausdorff gaps''' are indestructible under cardinal preserving extensions (property 5 above) are due to Kunen (unpublished).}} #* {{jêder-kitêb/ceribandin|en |sal=1925|nivîskar={{w|F. Scott Fitzgerald|F[rancis] Scott Fitzgerald}}|beş=IX|sernav=w:The Great Gatsby|cih=New York, N.Y.|weşanxane=w:Charles Scribner's Sons|oclc=884653065|sal2=1953|cih2=New York, N.Y.|weşanxane2=Charles Scribner’s Sons|isbn2=978-0-684-16498-4|rûpel2=182|urlya-rûpelê2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|jêgirtin=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.|lccn=gm 95-1}} #* {{jêder-kitêb/ceribandin|en |nivîskar=w:John Boyle O'Reilly|beş=On the Trail|sernav=[[w:Moondyne|Moondyne: A Story from the Under-world]]|cih=London|weşanxane=[[w:Routledge|George Routledge and Sons]]|sal=1878|sala-weşandinê=1879|oclc=39983928|sernav2=Moondyne: A Story of Convict Life in Australia|cih2=London|weşanxane2=George Routledge & Sons, Limited, Broadway House, {{w|Ludgate Hill}}|sal2=[1880s]|qisim2=book first|pages2=23–24|urlya-rûpelê2=https://archive.org/stream/moondynestory00oreirich#page/24/mode/1up/|oclc2=83033698|jêgirtin=It was sore travelling for horse and man under the blazing sun, with no food nor water save what he pressed from the pith of the palms, and even these were growing scarce. The only life on the plains was the hard and dusty scrub. Every hour brought a more hopeless and '''grislier''' desolation.}} #* {{jêder-kitêb/ceribandin|en |sal=1966|sala-orîjînal=1858|nivîskar=w:Hans Christian Andersen|terciman=Pat Shaw Iversen|orîjînal=Suppe paa en Pølsepind|zimanê-orîjînal=da|beş=Soup from a Sausage Peg|sernav=The Snow Queen and Other Tales|serî=Signet Classic|cildê-seriyê=CT334|cih=New York, N.Y.|weşanxane=w:New American Library|rûpel=224|urlya-rûpelê=http://books.google.com/books?id=2tjWAAAAMAAJ&q=%22from+your+own+nook+and+cranny%22#search_anchor|oclc=636818779|metna-orîjînal=da:Det er underligt at komme hjemme fra sin egen '''Krinkelkrog''', gaae med Skib, der ogsaa er en Slags '''Krinkelkrog''', og saa pludselig være over hundrede Mile borte og staae i et fremmedt Land.|jêgirtin=It’s strange to come away from home, from your own '''nook and cranny''', to go by ship – which is also a kind of '''nook and cranny''' – and then suddenly be more than a hundred miles away and stand in a foreign land!}} #* {{jêder-kitêb/ceribandin|en |sal=2013|nivîskar=Bernd Klauer; Reiner Manstetten; Thomas Petersen; Johannes Schiller|zimanê-eserê=de|beş=Materielle Bestände|sernav=Die Kunst, langfristig zu denken : Wege zur Nachhaltigkeit<t:The art of thinking long-term : paths to sustainability>|cih=Baden-Baden|weşanxane=Nomos|versiyona-nû=translated as|terciman2=Kathleen Cross|sal2=2016|sernav2=Sustainability and the Art of Long-Term Thinking|beş2=Material stocks|cih2=London; New York|weşanxane2=Routledge, Taylor & Francis Group|qisim2=4.3.1. Material sets and stocks|rûpel2=44|isbn2=9781134986187|jêgirtin=The property of '''appertainment''' of a set is therefore the property which, by definition, has to be given in every element of the set.}} #* {{jêder-kitêb/ceribandin|en |nivîskar={{w|Andrew Seth Pringle-Pattison|Andrew Seth}}|sernav=Hegelianism and Personality|çap=2nd|sal=1887|sala-weşandinê=1893|rûpel=216|rêz-sade=footnote|cih=Edinburgh; London|weşanxane=William Blackwood and Sons|versiyona-nû=quoted in|sal2=1893|2nivîskar=w:David George Ritchie|sernav2=Darwin and Hegel, with other Philosophical Studies|cih2=London|weşanxane2=S. Sonnenschein & Co.; New York, N.Y.: [[w:Macmillan Publishers (United States)|Macmillan & Co.]]|rûpel2=72|urlya-rûpelê2=http://books.google.com/books?id=8yGEAHvAwiIC&q=%22An+absolute+system+cannot+afford+to+leave+any+nook+or+cranny+of+existence+unexplored%22#search_anchor|rêz-sade2=note 1|oclc2=3299658|jêgirtin=An absolute system cannot afford to leave '''any nook or cranny''' of existence unexplored.}} #* {{jêder-kitêb/ceribandin|en |nivîskar=w:David George Ritchie|jê-tê-girtin=w:Andrew Seth Pringle-Pattison|sernav=Hegelianism and Personality|çap=2nd|kesên-din=1893, page 216, footnote|cihê-jêgirtinê=Darwin and Hegel, with other Philosophical Studies|cih=London|weşanxane=S. Sonnenschein & Co.; New York, N.Y.: [[w:Macmillan Publishers (United States)|Macmillan & Co.]]|sal=1893|rûpel=72|rêz-sade=note 1|urlya-rûpelê=http://books.google.com/books?id=8yGEAHvAwiIC&q=%22An+absolute+system+cannot+afford+to+leave+any+nook+or+cranny+of+existence+unexplored%22#search_anchor|oclc=3299658|jêgirtin=An absolute system cannot afford to leave '''any nook or cranny''' of existence unexplored.}} #* {{jêder-kovar/ceribandin|en |nivîskar=w:Hayden Carruth|sernav=Making It New|kovar=w:The Hudson Review|cih=New York, N.Y.|weşanxane=Hudson Review, Inc.|meh=Summer|sal=1968|cild=XXI|hejmar=2|issn=2325-5935|oclc=920393805|versiyona-nû=reprinted as|cure2=book|beş2=From ‘Making It New’ [''Body Rags'']|edîtor2=Howard Nelson|sernav2=On the Poetry of Galway Kinnell: The Wages of Dying|serî2=Under Discussion|cih2=Ann Arbor, Mich.|weşanxane2={{w|University of Michigan Press}}|sal2=1987|rûpel2=75|urlya-rûpelê2=https://books.google.com/books?id=7cXV26PmJUYC&pg=PA75|isbn2=978-0-472-09376-2|jêgirtin=In his &#91;{{w|Galway Kinnell}}'s&#93; new book, ''Body Rags'', he has brought this style to a kind of perfection, especially in two poems about the killing of animals, "The Porcupine" and "The Bear." These are the '''grisliest''' poems I have ever read.}} #* {{jêder-kitêb/ceribandin|fr |sal=1973|nivîskar=w:Claude Simon|sernav=Tryptique|weşanxane=Éditions de Minuit|rûpel=12|jêgirtin=Les sons de la cloche '''égrenant''' les quarts, les demies et les heures {{...}}|t=The sounds of the clock '''rattling off''' the quarters, the halves and the hours {{...}}}} #* {{jêder-kitêb/ceribandin|de |sal=1999|nivîskar=Paul Hoffmann|hevnivîskar=Stefan H. Brandenburger; Ulrike Brauner; Thomas Hieke|sernav=Q 22:28, 30: You Will Judge the Twelve Tribes of Israel|serî=Documenta Q: Reconstructions of Q Through Two Centuries of Gospel Research Excerpted, Sorted and Evaluated|cildê-seriyê=volume 5|isbn=90-429-0734-7|rûpel=189|jêgirtin=Zumal in beiden Fällen eine Partizipialform vorliegt, liegt es nahe, die '''Zeitworte''' als Varianten anzusehen.}} #* {{jêder-kitêb/ceribandin|ru |nivîskar=lw:ru:[[Филарет (Дроздов)|Митрополи́т Филаре́т]]<tr:-><t:Mitropolit Filaret>|beş=ru:Сло́во предъ приведеніемъ къ присягѣ Санктпетербурксаго дворя́нства и гражданъ для избранія суде́й|tarîx=1818-02-01<!--Converted to Gregorian-->|sala-weşandinê=1873|cih=Moscow|sernav=ru:Сочинеиія Филарета Митрополита Московскаго и Коломенскаго|cild=I|rûpel=253|urlya-rûpelê=https://books.google.com/books?id=yWEcAAAAMAAJ&pg=PA253|jêgirtin=[[изъ|Изъ]] [[сей|сего́]] [[ви́дно]], [[что]] [[судья|судія́]], [[как|какъ]] [[блюсти́тель]] [[общественный|обще́ственнаго]] [[порядок|поря́дка]], [[должен|долженъ]] '''объима́ть''' [[и]] [[проника́ть]] [[мысль|мы́слію]] [[весь]] [[состав|соста́въ]] [[общество|о́бщества]];|t=From this it becomes discernible that a judge, in his function as a warden of public order, should '''enclose''' and traverse the whole body of the society;}} #* {{jêder-kitêb/ceribandin|yi |nivîskar=I.J. Singer|sernav=Fun a velt vos iz nishto mer|sal=1946|beş=yi:אַ ייד אַ מלמד פֿאַרגלוסט זיך צו ווערן אַ מלאך אום פּורים און פֿאַרלירט, נעבעך, אַן אויג<subst:ייד/ייִד,מלמד/מעלאַמעד,פּורים/פּורעם,מלאך/מאַלעך><t:A Jew, a teacher, decides to become an angel on Purim and, unfortunately, loses an eye>|jêgirtin=יעדן '''זמן''' אויפֿסניי, חול־המועד סוכות אָדער חול־המועד פּסח, בין איך געווען זייער האָפֿערדיק, וואָס מען האָט מיך גענומען איין מלמד און אָנגעדונגען צו אַן אַנדערן, גלײבנדיק, אַז דער נייער וועט זיין בעסער ווי דער אַלטער.|subst=זמן//זמאַן,חול־המועד//כאַלעמויעד,סוכות//סוקוס,פּסח//פּייסעך,מלמד//מעלאַמעד,נייער//נײַער|tercime=Every '''term''' anew, during the intermediary periods of Sukkot and Pesach, I was very confident in being taken from one melamed and given to another, believing the new one would be better than the old.}} #* {{jêder-kitêb/ceribandin|ru |sal=1983|nivîskar=w:Samuil Marshak|sernav=Баллада о королевском бутерброде<subst:бутер/бутэр><t:Ballad of the King's Bread>|orîjînal=The King's Breakfast|ji-aliyê=w:A. A. Milne|metna-orîjînal=en:"Nobody," / He whimpered, / "Could call me / A fussy man; / I only want / A little bit / Of butter for / My bread!"|zimanê-orîjînal=en|jêgirtin=— [[никто|Никто́]] [[не]] [[сказать|ска́жет]], [[бу́дто]] [[я]] / [[тиран|Тира́н]] [[и]] [[сумасбро́д]], / [[за|За]] [[то́]], [[что]] [[к]] [[чай|ча́ю]] [[я]] [[любить|люблю́]] / [[хороший|Хоро́ший]] '''бутербро́д'''.|subst=бутербро́д/бутэрбро́д|t="No one would say that I / Am a tyrant and madman, / For the fact that with my tea I like / A good '''bread-and-butter sandwich'''."}} #* {{jêder-kitêb/ceribandin|ota |sal=1922|sernav=ota:ادارهٔ حرب و سیاست<tr:Idare-i harb ve siyaset>|cih=Istanbul|weşanxane=ota:مطبعهٔ عثمانیه|rûpel=176|urlya-rûpelê=https://babel.hathitrust.org/cgi/pt?id=uc1.aa0006749931&view=1up&seq=182|versiyona-nû=translation of|ziman2=de|sal2=1922|2nivîskar=w:Erich Ludendorff|sernav2=Kriegsführung und Politik|cih2=Berlin|weşanxane2=E. S. Mittler & Sohn|rûpel2=199|urlya-rûpelê2=https://archive.org/details/kriegfhrungund00lude/page/199|jêgirtin=شرقی غالیچیا، و بوقوونیاده شمندوفر نقاباتی تأسس ایتدكدن صكره شرقده '''صلحی''' تأمین ایچون دینیه‌ستر نهری جنوبندن روس — رومن جبهه‌سنه قارشو حربه دوام ایتمك فكرندن فراغت ایتمك لازمكلیورایدی. بر قصقاچ تأثیری یاپه‌بیلمك ایچون بری قلاص غربندن یوقاره‌سه‌ره‌ت اوزرینه و دیكری دینیه‌ستر جنوبندن اولمق اوزره مضاعف بر تعرض اجراسی لازمكلیور ایده.|tr=Şarki Galiçya, ve Bukovinyada‌ şömendüfer nakabatı tesis itdikden soñra şarkda sulhı temin içün Dinyester nehri cenubundan rus-rumen cebhesine karşu harbe devam itmek fikrinden feragat itmek lazım geliyor idi. Bir kıskaç tesiri yapabilmek içün biri Kalas garbından yukara-yı Seret üzerine ve diğeri Dinyester cenubundan olmak üzere muzaaf bir taarruz icrası lazım geliyor idi.|metna-orîjînal=de:Unsere weitere Absicht, nach Herstellung der Eisenbahnverbindungen in Ostgalizien und in der Bukowina zur Erzwingung des Friedens im Osten den Feldzug gegen die russisch-rumänische Front südlich des Dnjestr fortzusetzen, mußte aufgegeben werden. Er sollte in einem doppelten Angriff über den oberen Sereth westlich Galatz und hart südlich des Dnjestr bestehen und auch zangenartig wirken.|t=Our further intention, the military campaign against the Russian-Romanian front to be continued south of the Dniestr to force '''peace''' in the east, after procurement of the railroad connections in Eastern Galicia and in the Bukovina, had to be abandoned. It was supposed to consist in a double attack over the upper Siret west of Galați as well as hard south of the Dniestr and go like pincers.}} #* {{jêder-kitêb/ceribandin|it |sernav=L'œuvre complète de Tchouang-tseu|terciman=Liou Kia-hway<f:Hant:劉家槐><f:Pinyin:Liú Jiāhuái>|orîjînal={{w|Zhuangzi (book)|Zhuang-zi}}|ji-aliyê=莊周<t:Zhuang Zhou>|zimanê-orîjînal=och|sala-orîjînal=c. 300 {{BCE}}|sal=1969|sal2=1982|sala-weşandinê2=2010|versiyona-nû=translated as|terciman2=Carlo Laurenti; Christine Leverd|sernav2=Zhuang-zi [Chuang-tzu]|serî2=Gli Adelphi|cildê-seriyê2=41|beş2=Libertà naturale|tercimeya-beşê2=Natural Freedom|çap2=6th|cih2=Milan|weşanxane2={{w|Adelphi Edizioni}}|rûpel2=17|isbn2=978-88-459-0950-4|metna-orîjînal=och:之人也,物莫之傷,大浸稽天而不溺,大旱、金石流、土山焦而不熱。|jêgirtin=Se le acque si alzassero fino al cielo, non annegherebbero. Se la siccità '''liquefacesse''' i metalli e infiammasse le montagne, non ne sarebbero neppure sfiorati.|t=If the waters were to rise to the skies, they wouldn't drown. If drought were to '''liquefy''' metals, and set fire to the mountains, they wouldn't even be touched by it.}} #* {{jêder-kitêb/ceribandin|fr |zimanê-termê=en|sal=1677|nivîskar=Guy Miege [''i.e.'', [[w:fr:Guy Miège|Guy Miège]]]|made=HAGGESS|urlya-madeyê=https://books.google.com/books?id=YbWtTwVbLioC&pg=PP857|sernav=A New Dictionary French and English, with Another English and French; According to the Present Use, and Modern Orthography of the French. Inrich’d with New Words, Choice Phrases, and Apposite Proverbs; Digested into a Most Accurate Method; and Contrived for the Use Both of English and Foreiners|cih=London|weşanxane=Printed by Tho[mas] Dawks, for Thomas Basset, at the George, near [[w:Clifford's Inn|Cliffords-inn]], in [[w:Fleet Street|Fleetstreet]]|stûn=3|oclc=874955677|jêgirtin='''HAGGESS''', '''haggas''', or '''haggis''', ſort d'appret fait avec des herbes, du larde caupé menu, des épices, des œufs, & du fromage, le tout mis enſemble & bouilli dans une panſe de brebis.|t='''HAGGESS''', '''haggas''', or '''haggis''', a sort of preparation made with herbs, bacon, spices, eggs, cheese, all put together and boiled in a sheep's belly.|brackets=1}} #*{{jêder-kovar/ceribandin|en|sernav=Art VIII.—Who are the True Conservatives?|kovar=The Quarterly Christian Spectator|cih=New Haven, Conn.|weşanxane=Published for the proprietor by Hezekiah Howe [...]|meh=November|sal=1838|cild=X|hejmar=IV|rûpel=608|urlya-rûpelê=https://books.google.com/books?id=S7MAAAAAMAAJ&pg=PA608|oclc=5090641|jêgirtin=They have guarded a few minds against '''sciolism''' and pretension in philosophy, and they have given a new and more imposing air to '''sciolism''' itself, and placed in the hands of the charlatan enchantments and magical arts, which hold fast its victims with a potency never granted to an inferior spell. There is no '''sciolism''' now which is more dangerous than that which is so very careful to inform us of its entire freedom from all that is superficial, {{...}}}} #*{{jêder-kovar/ceribandin|en |tarîx=March 8, 2023|paşnav=Wu|pêşnav=Huizhong|nivîskar2=Johnson Lai|sernav=Taiwan suspects Chinese ships cut islands’ internet cables|kovar={{w|The Washington Post}}|urlya-arşîvê=https://web.archive.org/web/20230311220100/https://www.washingtonpost.com/business/2023/03/08/matsu-taiwan-internet-cables-cut-china/d322fcae-bd70-11ed-9350-7c5fccd598ad_story.html|tarîxa-arşîvê=11 March 2023|issn=0190-8286|oclc=2269358|qisim=Business|tarîx2=April 18, 2023|sernav2=Chinese ships cut internet of Taiwan’s outlying islands|cure2=journal|kovar2={{w|AP News}}|urlya-arşîvê2=https://web.archive.org/web/20230308053048/https://apnews.com/article/matsu-taiwan-internet-cables-cut-china-65f10f5f73a346fa788436366d7a7c70|jêgirtin=Wang Chung Ming, the head of '''Lienchiang''' County, as the Matsu islands are officially called, said he and the legislator from Matsu went to Taipei shortly after the internet broke down to ask for help, and was told they would get priority in any future internet backup plans.{{...}}<br>Wang, the head of '''Lienchiang''' County, said he had mentioned the cables on a recent visit to China, where he had met an executive from China Mobile.}} #*{{jêder-kovar/ceribandin|en |sal=1994|meh=April|kovar=光華|tercimeya-kovar=Sinorama|url=https://books.google.com/books?id=GIRDAAAAYAAJ|cild=19|hejmar=1-6|weşanxane={{w|Government Information Office}}|issn=0256-9043|oclc=20549278|sal2=1994|meh2=April|2nivîskar=Yuan-ching Cheng|cure2=journal|sernav2=Kinmen- -From Battlefield to Tourist Destination|kovar2=台灣光華雜誌<t:Taiwan Panorama>|terciman2=Robert Taylor|urlya-arşîvê2=https://web.archive.org/web/20221021103917/https://www.taiwan-panorama.com/en/Articles/Details?Guid=c9335c12-7ec3-40e7-96be-33a81416a223|tarîxa-arşîvê2=21 October 2022|qisim2=Around Taiwan|jêgirtin=The temple to Colonel Li Kuang-chien lies near Hsiputou village in the northwest of Kinmen Island, around five kilometers from the town of '''Chincheng''' and next to the famous Hill 132.{{...}}<br>Because Kinmen was settled early, it bears many traces of the past. There are 21 listed historical monuments scattered all over the island. The only Grade I monument among them is the "Chiehhsiao Arch," commemorating Chiu Liang-kung's mother, located in East Houpu Street in the center of '''Chincheng'''.}} == {{ziman|ku}} == === Lêker === [[Wêne:Example.jpg|thumb|Mînak]] {{lêker|ku}} # [[testsdfsdafdasfas]] #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Mîr Kamûran di sala 1947 an de li '''Fransayê''' bicîh bû, û heta 1970ê li Xwendegeha zimanên rojhilatî ya Zaningeha Parîsê Profesorîya ziman û çanda Kurdî kir.|nivîskar=Kamuran A.Bedir Khan|nivîskar2=Josefe Bertolino|nivîskar3=Kendal Nezan|sernav=Institut Kurde De Paris Ferhenga Kurdî-Fransizî|cih=Istanbul|weşanxane={{w|Weşanên Avesta|Avesta}}|sal=2017|çap=1|rûpel=XXXIX|isbn=978-2-360134311}} #* {{jêder-malper/ceribandin|ku|jêgirtin=Li gor amareke ajanseke selametiya xorakê, xelkê '''Fransa''' salane nêzîkî 4,000 ton Beqan dixwin, ku ew jî bi qasî goştê 40 nehengên şîn e. Ji bo dabînkirina pêwîstiya bazarê jî, li '''Fransayê''' kêlgeh û cihên taybet ên xwedîkirina Beqan hene. Beşek ji wêneyên di vê wênenûçeyê de, kêlgeha xwedîkirina Beqan li navçeyeke '''Fransayê''' ye, herwiha beşek ji wêneyan jî awayê patin û çêkirina xwarinê ji goştê Beqan di xwaringehan de û pêşkêşkirina wan bi mêvanan nîşan didin.|nivîskar=ciwan ikbal|sernav=Fransî di salekê de 4,000 ton Beqan dixwin|url=https://www.rudaw.net/kurmanci/world/101020201|dîrok=10.10.2020}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Bale awayeke dansê ye. Babetek ji şanoya musîkal e. Ji bo dansa baleyê, divê meriv cansivik û nerm be. Loma jî bale gelekî antremanê dixwaze û divê meriv di temenekî biçûk de dest pê bike. Di baleyê de, meriv li gor livên diyarkirî dansê dike. Hinek ji wan tu di wêne de dibînî. Ji ber ku bale li '''Fransayê''' bi pêş ketiye, loma navên livên wê jî bi [[fransî]] ne.|amadekar=Komxebat|sernav=Ansîklopediya Zarokan|cih=Denmark<br>|weşanxane={{w|Weşanên APECl|APEC}}|sal=2004|rûpel=25|isbn=91-89675-32-0}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala-weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani|weşanxane=BRILL|ISBN=9789004754430|rûpel=383}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala-weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji %&nbsp;70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala-weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}} #* {{jêder-malper/ceribandin|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|tarîx=2021-05-04}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|sal=2013|weşanxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}} #* {{jêder-malper/ceribandin|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|tarîx=2025-02-02|jêgirtin=adfasdf}} #* {{jêder-kitêb/ceribandin|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}} # Maneya 3an. r9qsk0xbowatxlsiv0ra8t9f9bcuz6m 6291565 6291564 2026-07-26T14:31:39Z Wikihez 7344 6291565 wikitext text/x-wiki {{/vê rêzê neguhêre}} == {{ziman|en}} == === Lêker === [[Wêne:Example.jpg|thumb|Mînak]] {{lêker|en}} # Maneya 1ê. #: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}} #* {{jêder-kovar/ceribandin|en|nivîskar=Justin Chang|lînka-nivîskar=Justin Chang|sernav=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|urlya-arşîvê=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|tarîxa-arşîvê=28 June 2017|kovar=[[w:Variety (magazine)|Variety]]|tarîx=25 May 2011|issn=0042-2738|oclc=960911146|jêgirtin=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}} #* {{jêder-kitêb/ceribandin|en|nivîskar=F[rancis] Scott Fitzgerald|lînka-nivîskar=F. Scott Fitzgerald|beş=IX|sernav=[[w:The Great Gatsby|The Great Gatsby]]|cih=New York, N.Y.|weşanxane=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|sal=1925|oclc=884653065|cih2=New York, N.Y.|weşanxane2=Charles Scribner’s Sons|sal2=1953|isbn2=978-0-684-16498-4|rûpel2=182|urlya-rûpelê2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|jêgirtin=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}} #* {{jêder-malper/ceribandin|en|nivîskar=Meg Downey|sernav=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|urlya-arşîvê=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|tarîxa-arşîvê=24 June 2018|xebat=[[w:Polygon (website)|Polygon]]|tarîx=7 May 2018|jêgirtin=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}} #* {{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=The Republic of Molossia has played a considerable role in developing '''intermicronational''' diplomacy.}} #* {{jêder-kitêb/ceribandin|en |sal=c. 1988|nivîskar=Peter W. Rodino, Jr.; et al.|sernav=Report of the Congressional Committees Investigating the Iran/Contra Affair|urlya-rûpelê=http://books.google.com/books?id=ew_K3auTwEgC&pg=PA646&dq=phone+call|rûpel=646|jêgirtin=On November 20, they had a '''phone call''' and later a meeting with others lasting at least 1 1/2 hours.}} #* {{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=Centrally acting '''sympathicolytics''' cause a decrease in the sympathetic outflow from the brainstem. This reduction in sympathetic outflow brings about vasodilatation of the peripheral vascular bed with a concomitant drop in the blood pressure}} #* {{jêder-kitêb/ceribandin|en |sal=2005|edîtor=Martin Endress; George Psathas; Hisashi Nasu|beş=Between the Everyday Life-World and the World of Social Scientific Theory—Towards an “Adequate” Social Thory|nivîskar=Hisashi Nasu|sernav=Explorations of the Life-World: Continuing Dialogues with Alfred Schutz|weşanxane=Springer|isbn=1402032196|rûpel=136|jêgirtin=It is not only the '''man on the street''' who is characterized by taking one's own system of relevance for granted. This holds true for the expert,....}} #* {{jêder-kitêb/ceribandin|en |sal=2002|nivîskar=Peter Shirlow|beş=Sinn Féin: Beyond; within Containment|hejmara-beşê=4|edîtor=Stefan Wolff; Jörg Neuheiser|sernav=Peace at Last?: The Impact of the Good Friday Agreement on Northern Ireland|weşanxane=Berghahn Books|isbn=157181518X|urlya-rûpelê=http://books.google.com/books?id=QyInASKCFjcC&pg=PA62&dq=ethnosectarian&sig=Q54T1Vz3vsKnqd05666N1ddFki8|rûpel=62|jêgirtin=Moreover, language, discourse, and dialogue, their definition and mobilisation, create chains of equivalence which enclose subjects within not only '''ethnosectarian''' but at times entirely self-referential and self-sustained notions of identity, practice and collective devotion.}} #* {{jêder-kitêb/ceribandin|en |sernav=Extreme Hydrological Events: New Concepts for Security|edîtor=O.F. Vasiliev; P.H.A.J.M. van Gelder; E.J. Plate; M.V. Bolgov|sal=2007|weşanxane=[[w:Springer Science+Business Media|Springer]]|isbn=1402057393|beş=Predetermination of Floods|nivîskar=Pierre Hubert; Ioulia Tchiguirinskaia; Daniel Schertzer; Hocine Bendjoudi; Shaun Lovejoy|qisim=On Probability and Return Periods of Floods|hejmara-qismê=2|rûpel=187|jêgirtin=It is perfectly legitimate to be interested, particularly for safety studies, by millennium floods or floods or '''decamillennium''' floods (i.e. having a probability 0,001 and 0,0001 respectively to occur in the course of given year) of a river which did not exist five thousand years ago and that will not exist perhaps any more in ten thousand years, similarly to the industrial interest in the probability of faults which will not undoubtedly occur, because the minimization of their probability is an aspect of a policy of industrial security.}} #* {{jêder-kitêb/ceribandin|en |sal=1995|nivîskar=w:Robert M. Solovay|nivîskarê-sereke=w:Kurt Gödel|edîtor=w:Solomon Feferman; w:John W. Dawson Jr.; w:Warren Goldfarb; {{w|Charles Parsons (philosopher)|Charles Parsons}}; w:Robert M. Solovay|beş=Notes to *1970 &#91;{{w|Godel's ontological proof}}&#93;|sernav=Kurt Gödel: Collected Works: Volume III|urlya-rûpelê=https://books.google.com.au/books?id=gDzbuUwma5MC&pg=PA419&dq=%22Hausdorff+gap%22%7C%22Hausdorff+gaps%22&hl=en&newbks=1&newbks_redir=0&sa=X&ved=2ahUKEwjo-o7D9OT7AhVQJUQIHaSlBIgQ6AF6BAhXEAI#v=onepage&q=%22Hausdorff%20gap%22%7C%22Hausdorff%20gaps%22&f=false|rûpel=419|weşanxane=w:Oxford University Press|jêgirtin=Of course, Hausdorff did not talk of models of set theory or prove absoluteness results. He gave a direct construction in ''ZFC'' of what is now called a '''Hausdorff gap''' and proved properties 1 through 4 for his construction. The notion of a '''Hausdorff gap''' and the proof that '''Hausdorff gaps''' are indestructible under cardinal preserving extensions (property 5 above) are due to Kunen (unpublished).}} #* {{jêder-kitêb/ceribandin|en |sal=1925|nivîskar={{w|F. Scott Fitzgerald|F[rancis] Scott Fitzgerald}}|beş=IX|sernav=w:The Great Gatsby|cih=New York, N.Y.|weşanxane=w:Charles Scribner's Sons|oclc=884653065|sal2=1953|cih2=New York, N.Y.|weşanxane2=Charles Scribner’s Sons|isbn2=978-0-684-16498-4|rûpel2=182|urlya-rûpelê2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|jêgirtin=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.|lccn=gm 95-1}} #* {{jêder-kitêb/ceribandin|en |nivîskar=w:John Boyle O'Reilly|beş=On the Trail|sernav=[[w:Moondyne|Moondyne: A Story from the Under-world]]|cih=London|weşanxane=[[w:Routledge|George Routledge and Sons]]|sal=1878|sala-weşandinê=1879|oclc=39983928|sernav2=Moondyne: A Story of Convict Life in Australia|cih2=London|weşanxane2=George Routledge & Sons, Limited, Broadway House, {{w|Ludgate Hill}}|sal2=[1880s]|qisim2=book first|pages2=23–24|urlya-rûpelê2=https://archive.org/stream/moondynestory00oreirich#page/24/mode/1up/|oclc2=83033698|jêgirtin=It was sore travelling for horse and man under the blazing sun, with no food nor water save what he pressed from the pith of the palms, and even these were growing scarce. The only life on the plains was the hard and dusty scrub. Every hour brought a more hopeless and '''grislier''' desolation.}} #* {{jêder-kitêb/ceribandin|en |sal=1966|sala-orîjînal=1858|nivîskar=w:Hans Christian Andersen|terciman=Pat Shaw Iversen|orîjînal=Suppe paa en Pølsepind|zimanê-orîjînal=da|beş=Soup from a Sausage Peg|sernav=The Snow Queen and Other Tales|serî=Signet Classic|cildê-seriyê=CT334|cih=New York, N.Y.|weşanxane=w:New American Library|rûpel=224|urlya-rûpelê=http://books.google.com/books?id=2tjWAAAAMAAJ&q=%22from+your+own+nook+and+cranny%22#search_anchor|oclc=636818779|metna-orîjînal=da:Det er underligt at komme hjemme fra sin egen '''Krinkelkrog''', gaae med Skib, der ogsaa er en Slags '''Krinkelkrog''', og saa pludselig være over hundrede Mile borte og staae i et fremmedt Land.|jêgirtin=It’s strange to come away from home, from your own '''nook and cranny''', to go by ship – which is also a kind of '''nook and cranny''' – and then suddenly be more than a hundred miles away and stand in a foreign land!}} #* {{jêder-kitêb/ceribandin|en |sal=2013|nivîskar=Bernd Klauer; Reiner Manstetten; Thomas Petersen; Johannes Schiller|zimanê-eserê=de|beş=Materielle Bestände|sernav=Die Kunst, langfristig zu denken : Wege zur Nachhaltigkeit<t:The art of thinking long-term : paths to sustainability>|cih=Baden-Baden|weşanxane=Nomos|versiyona-nû=translated as|terciman2=Kathleen Cross|sal2=2016|sernav2=Sustainability and the Art of Long-Term Thinking|beş2=Material stocks|cih2=London; New York|weşanxane2=Routledge, Taylor & Francis Group|qisim2=4.3.1. Material sets and stocks|rûpel2=44|isbn2=9781134986187|jêgirtin=The property of '''appertainment''' of a set is therefore the property which, by definition, has to be given in every element of the set.}} #* {{jêder-kitêb/ceribandin|en |nivîskar={{w|Andrew Seth Pringle-Pattison|Andrew Seth}}|sernav=Hegelianism and Personality|çap=2nd|sal=1887|sala-weşandinê=1893|rûpel=216|rêz-sade=footnote|cih=Edinburgh; London|weşanxane=William Blackwood and Sons|versiyona-nû=quoted in|sal2=1893|2nivîskar=w:David George Ritchie|sernav2=Darwin and Hegel, with other Philosophical Studies|cih2=London|weşanxane2=S. Sonnenschein & Co.; New York, N.Y.: [[w:Macmillan Publishers (United States)|Macmillan & Co.]]|rûpel2=72|urlya-rûpelê2=http://books.google.com/books?id=8yGEAHvAwiIC&q=%22An+absolute+system+cannot+afford+to+leave+any+nook+or+cranny+of+existence+unexplored%22#search_anchor|rêz-sade2=note 1|oclc2=3299658|jêgirtin=An absolute system cannot afford to leave '''any nook or cranny''' of existence unexplored.}} #* {{jêder-kitêb/ceribandin|en |nivîskar=w:David George Ritchie|jê-tê-girtin=w:Andrew Seth Pringle-Pattison|sernav=Hegelianism and Personality|çap=2nd|kesên-din=1893, page 216, footnote|cihê-jêgirtinê=Darwin and Hegel, with other Philosophical Studies|cih=London|weşanxane=S. Sonnenschein & Co.; New York, N.Y.: [[w:Macmillan Publishers (United States)|Macmillan & Co.]]|sal=1893|rûpel=72|rêz-sade=note 1|urlya-rûpelê=http://books.google.com/books?id=8yGEAHvAwiIC&q=%22An+absolute+system+cannot+afford+to+leave+any+nook+or+cranny+of+existence+unexplored%22#search_anchor|oclc=3299658|jêgirtin=An absolute system cannot afford to leave '''any nook or cranny''' of existence unexplored.}} #* {{jêder-kovar/ceribandin|en |nivîskar=w:Hayden Carruth|sernav=Making It New|kovar=w:The Hudson Review|cih=New York, N.Y.|weşanxane=Hudson Review, Inc.|meh=Summer|sal=1968|cild=XXI|hejmar=2|issn=2325-5935|oclc=920393805|versiyona-nû=reprinted as|cure2=book|beş2=From ‘Making It New’ [''Body Rags'']|edîtor2=Howard Nelson|sernav2=On the Poetry of Galway Kinnell: The Wages of Dying|serî2=Under Discussion|cih2=Ann Arbor, Mich.|weşanxane2={{w|University of Michigan Press}}|sal2=1987|rûpel2=75|urlya-rûpelê2=https://books.google.com/books?id=7cXV26PmJUYC&pg=PA75|isbn2=978-0-472-09376-2|jêgirtin=In his &#91;{{w|Galway Kinnell}}'s&#93; new book, ''Body Rags'', he has brought this style to a kind of perfection, especially in two poems about the killing of animals, "The Porcupine" and "The Bear." These are the '''grisliest''' poems I have ever read.}} #* {{jêder-kitêb/ceribandin|fr |sal=1973|nivîskar=w:Claude Simon|sernav=Tryptique|weşanxane=Éditions de Minuit|rûpel=12|jêgirtin=Les sons de la cloche '''égrenant''' les quarts, les demies et les heures {{...}}|t=The sounds of the clock '''rattling off''' the quarters, the halves and the hours {{...}}}} #* {{jêder-kitêb/ceribandin|de |sal=1999|nivîskar=Paul Hoffmann|hevnivîskar=Stefan H. Brandenburger; Ulrike Brauner; Thomas Hieke|sernav=Q 22:28, 30: You Will Judge the Twelve Tribes of Israel|serî=Documenta Q: Reconstructions of Q Through Two Centuries of Gospel Research Excerpted, Sorted and Evaluated|cildê-seriyê=volume 5|isbn=90-429-0734-7|rûpel=189|jêgirtin=Zumal in beiden Fällen eine Partizipialform vorliegt, liegt es nahe, die '''Zeitworte''' als Varianten anzusehen.}} #* {{jêder-kitêb/ceribandin|ru |nivîskar=lw:ru:[[Филарет (Дроздов)|Митрополи́т Филаре́т]]<tr:-><t:Mitropolit Filaret>|beş=ru:Сло́во предъ приведеніемъ къ присягѣ Санктпетербурксаго дворя́нства и гражданъ для избранія суде́й|tarîx=1818-02-01<!--Converted to Gregorian-->|sala-weşandinê=1873|cih=Moscow|sernav=ru:Сочинеиія Филарета Митрополита Московскаго и Коломенскаго|cild=I|rûpel=253|urlya-rûpelê=https://books.google.com/books?id=yWEcAAAAMAAJ&pg=PA253|jêgirtin=[[изъ|Изъ]] [[сей|сего́]] [[ви́дно]], [[что]] [[судья|судія́]], [[как|какъ]] [[блюсти́тель]] [[общественный|обще́ственнаго]] [[порядок|поря́дка]], [[должен|долженъ]] '''объима́ть''' [[и]] [[проника́ть]] [[мысль|мы́слію]] [[весь]] [[состав|соста́въ]] [[общество|о́бщества]];|t=From this it becomes discernible that a judge, in his function as a warden of public order, should '''enclose''' and traverse the whole body of the society;}} #* {{jêder-kitêb/ceribandin|yi |nivîskar=I.J. Singer|sernav=Fun a velt vos iz nishto mer|sal=1946|beş=yi:אַ ייד אַ מלמד פֿאַרגלוסט זיך צו ווערן אַ מלאך אום פּורים און פֿאַרלירט, נעבעך, אַן אויג<subst:ייד/ייִד,מלמד/מעלאַמעד,פּורים/פּורעם,מלאך/מאַלעך><t:A Jew, a teacher, decides to become an angel on Purim and, unfortunately, loses an eye>|jêgirtin=יעדן '''זמן''' אויפֿסניי, חול־המועד סוכות אָדער חול־המועד פּסח, בין איך געווען זייער האָפֿערדיק, וואָס מען האָט מיך גענומען איין מלמד און אָנגעדונגען צו אַן אַנדערן, גלײבנדיק, אַז דער נייער וועט זיין בעסער ווי דער אַלטער.|subst=זמן//זמאַן,חול־המועד//כאַלעמויעד,סוכות//סוקוס,פּסח//פּייסעך,מלמד//מעלאַמעד,נייער//נײַער|tercime=Every '''term''' anew, during the intermediary periods of Sukkot and Pesach, I was very confident in being taken from one melamed and given to another, believing the new one would be better than the old.}} #* {{jêder-kitêb/ceribandin|ru |sal=1983|nivîskar=w:Samuil Marshak|sernav=Баллада о королевском бутерброде<subst:бутер/бутэр><t:Ballad of the King's Bread>|orîjînal=The King's Breakfast|ji-aliyê=w:A. A. Milne|metna-orîjînal=en:"Nobody," / He whimpered, / "Could call me / A fussy man; / I only want / A little bit / Of butter for / My bread!"|zimanê-orîjînal=en|jêgirtin=— [[никто|Никто́]] [[не]] [[сказать|ска́жет]], [[бу́дто]] [[я]] / [[тиран|Тира́н]] [[и]] [[сумасбро́д]], / [[за|За]] [[то́]], [[что]] [[к]] [[чай|ча́ю]] [[я]] [[любить|люблю́]] / [[хороший|Хоро́ший]] '''бутербро́д'''.|subst=бутербро́д/бутэрбро́д|t="No one would say that I / Am a tyrant and madman, / For the fact that with my tea I like / A good '''bread-and-butter sandwich'''."}} #* {{jêder-kitêb/ceribandin|ota |sal=1922|sernav=ota:ادارهٔ حرب و سیاست<tr:Idare-i harb ve siyaset>|cih=Istanbul|weşanxane=ota:مطبعهٔ عثمانیه|rûpel=176|urlya-rûpelê=https://babel.hathitrust.org/cgi/pt?id=uc1.aa0006749931&view=1up&seq=182|versiyona-nû=translation of|ziman2=de|sal2=1922|2nivîskar=w:Erich Ludendorff|sernav2=Kriegsführung und Politik|cih2=Berlin|weşanxane2=E. S. Mittler & Sohn|rûpel2=199|urlya-rûpelê2=https://archive.org/details/kriegfhrungund00lude/page/199|jêgirtin=شرقی غالیچیا، و بوقوونیاده شمندوفر نقاباتی تأسس ایتدكدن صكره شرقده '''صلحی''' تأمین ایچون دینیه‌ستر نهری جنوبندن روس — رومن جبهه‌سنه قارشو حربه دوام ایتمك فكرندن فراغت ایتمك لازمكلیورایدی. بر قصقاچ تأثیری یاپه‌بیلمك ایچون بری قلاص غربندن یوقاره‌سه‌ره‌ت اوزرینه و دیكری دینیه‌ستر جنوبندن اولمق اوزره مضاعف بر تعرض اجراسی لازمكلیور ایده.|tr=Şarki Galiçya, ve Bukovinyada‌ şömendüfer nakabatı tesis itdikden soñra şarkda sulhı temin içün Dinyester nehri cenubundan rus-rumen cebhesine karşu harbe devam itmek fikrinden feragat itmek lazım geliyor idi. Bir kıskaç tesiri yapabilmek içün biri Kalas garbından yukara-yı Seret üzerine ve diğeri Dinyester cenubundan olmak üzere muzaaf bir taarruz icrası lazım geliyor idi.|metna-orîjînal=de:Unsere weitere Absicht, nach Herstellung der Eisenbahnverbindungen in Ostgalizien und in der Bukowina zur Erzwingung des Friedens im Osten den Feldzug gegen die russisch-rumänische Front südlich des Dnjestr fortzusetzen, mußte aufgegeben werden. Er sollte in einem doppelten Angriff über den oberen Sereth westlich Galatz und hart südlich des Dnjestr bestehen und auch zangenartig wirken.|t=Our further intention, the military campaign against the Russian-Romanian front to be continued south of the Dniestr to force '''peace''' in the east, after procurement of the railroad connections in Eastern Galicia and in the Bukovina, had to be abandoned. It was supposed to consist in a double attack over the upper Siret west of Galați as well as hard south of the Dniestr and go like pincers.}} #* {{jêder-kitêb/ceribandin|it |sernav=L'œuvre complète de Tchouang-tseu|terciman=Liou Kia-hway<f:Hant:劉家槐><f:Pinyin:Liú Jiāhuái>|orîjînal={{w|Zhuangzi (book)|Zhuang-zi}}|ji-aliyê=莊周<t:Zhuang Zhou>|zimanê-orîjînal=och|sala-orîjînal=c. 300 {{BCE}}|sal=1969|sal2=1982|sala-weşandinê2=2010|versiyona-nû=translated as|terciman2=Carlo Laurenti; Christine Leverd|sernav2=Zhuang-zi [Chuang-tzu]|serî2=Gli Adelphi|cildê-seriyê2=41|beş2=Libertà naturale|tercimeya-beşê2=Natural Freedom|çap2=6th|cih2=Milan|weşanxane2={{w|Adelphi Edizioni}}|rûpel2=17|isbn2=978-88-459-0950-4|metna-orîjînal=och:之人也,物莫之傷,大浸稽天而不溺,大旱、金石流、土山焦而不熱。|jêgirtin=Se le acque si alzassero fino al cielo, non annegherebbero. Se la siccità '''liquefacesse''' i metalli e infiammasse le montagne, non ne sarebbero neppure sfiorati.|t=If the waters were to rise to the skies, they wouldn't drown. If drought were to '''liquefy''' metals, and set fire to the mountains, they wouldn't even be touched by it.}} #* {{jêder-kitêb/ceribandin|fr |zimanê-termê=en|sal=1677|nivîskar=Guy Miege [''i.e.'', [[w:fr:Guy Miège|Guy Miège]]]|made=HAGGESS|urlya-madeyê=https://books.google.com/books?id=YbWtTwVbLioC&pg=PP857|sernav=A New Dictionary French and English, with Another English and French; According to the Present Use, and Modern Orthography of the French. Inrich’d with New Words, Choice Phrases, and Apposite Proverbs; Digested into a Most Accurate Method; and Contrived for the Use Both of English and Foreiners|cih=London|weşanxane=Printed by Tho[mas] Dawks, for Thomas Basset, at the George, near [[w:Clifford's Inn|Cliffords-inn]], in [[w:Fleet Street|Fleetstreet]]|stûn=3|oclc=874955677|jêgirtin='''HAGGESS''', '''haggas''', or '''haggis''', ſort d'appret fait avec des herbes, du larde caupé menu, des épices, des œufs, & du fromage, le tout mis enſemble & bouilli dans une panſe de brebis.|t='''HAGGESS''', '''haggas''', or '''haggis''', a sort of preparation made with herbs, bacon, spices, eggs, cheese, all put together and boiled in a sheep's belly.|brackets=1}} #*{{jêder-kovar/ceribandin|en|sernav=Art VIII.—Who are the True Conservatives?|kovar=The Quarterly Christian Spectator|cih=New Haven, Conn.|weşanxane=Published for the proprietor by Hezekiah Howe [...]|meh=November|sal=1838|cild=X|hejmar=IV|rûpel=608|urlya-rûpelê=https://books.google.com/books?id=S7MAAAAAMAAJ&pg=PA608|oclc=5090641|jêgirtin=They have guarded a few minds against '''sciolism''' and pretension in philosophy, and they have given a new and more imposing air to '''sciolism''' itself, and placed in the hands of the charlatan enchantments and magical arts, which hold fast its victims with a potency never granted to an inferior spell. There is no '''sciolism''' now which is more dangerous than that which is so very careful to inform us of its entire freedom from all that is superficial, {{...}}}} #*{{jêder-kovar/ceribandin|en |tarîx=March 8, 2023|paşnav=Wu|pêşnav=Huizhong|nivîskar2=Johnson Lai|sernav=Taiwan suspects Chinese ships cut islands’ internet cables|kovar={{w|The Washington Post}}|urlya-arşîvê=https://web.archive.org/web/20230311220100/https://www.washingtonpost.com/business/2023/03/08/matsu-taiwan-internet-cables-cut-china/d322fcae-bd70-11ed-9350-7c5fccd598ad_story.html|tarîxa-arşîvê=11 March 2023|issn=0190-8286|oclc=2269358|qisim=Business|tarîx2=April 18, 2023|sernav2=Chinese ships cut internet of Taiwan’s outlying islands|cure2=journal|kovar2={{w|AP News}}|urlya-arşîvê2=https://web.archive.org/web/20230308053048/https://apnews.com/article/matsu-taiwan-internet-cables-cut-china-65f10f5f73a346fa788436366d7a7c70|jêgirtin=Wang Chung Ming, the head of '''Lienchiang''' County, as the Matsu islands are officially called, said he and the legislator from Matsu went to Taipei shortly after the internet broke down to ask for help, and was told they would get priority in any future internet backup plans.{{...}}<br>Wang, the head of '''Lienchiang''' County, said he had mentioned the cables on a recent visit to China, where he had met an executive from China Mobile.}} #*{{jêder-kovar/ceribandin|en |sal=1994|meh=April|kovar=光華|tercimeya-kovar=Sinorama|url=https://books.google.com/books?id=GIRDAAAAYAAJ|cild=19|hejmar=1-6|weşanxane={{w|Government Information Office}}|issn=0256-9043|oclc=20549278|sal2=1994|meh2=April|2nivîskar=Yuan-ching Cheng|cure2=journal|sernav2=Kinmen- -From Battlefield to Tourist Destination|kovar2=台灣光華雜誌<t:Taiwan Panorama>|terciman2=Robert Taylor|urlya-arşîvê2=https://web.archive.org/web/20221021103917/https://www.taiwan-panorama.com/en/Articles/Details?Guid=c9335c12-7ec3-40e7-96be-33a81416a223|tarîxa-arşîvê2=21 October 2022|qisim2=Around Taiwan|jêgirtin=The temple to Colonel Li Kuang-chien lies near Hsiputou village in the northwest of Kinmen Island, around five kilometers from the town of '''Chincheng''' and next to the famous Hill 132.{{...}}<br>Because Kinmen was settled early, it bears many traces of the past. There are 21 listed historical monuments scattered all over the island. The only Grade I monument among them is the "Chiehhsiao Arch," commemorating Chiu Liang-kung's mother, located in East Houpu Street in the center of '''Chincheng'''.}} == {{ziman|ku}} == === Lêker === [[Wêne:Example.jpg|thumb|Mînak]] {{lêker|ku}} # [[testsdfsdafdasfas]] #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Mîr Kamûran di sala 1947 an de li '''Fransayê''' bicîh bû, û heta 1970ê li Xwendegeha zimanên rojhilatî ya Zaningeha Parîsê Profesorîya ziman û çanda Kurdî kir.|nivîskar=Kamuran A.Bedir Khan|nivîskar2=Josefe Bertolino|nivîskar3=Kendal Nezan|sernav=Institut Kurde De Paris Ferhenga Kurdî-Fransizî|cih=Istanbul|weşanxane={{w|Weşanên Avesta|Avesta}}|sal=2017|çap=1|rûpel=XXXIX|isbn=978-2-360134311}} #* {{jêder-malper/ceribandin|ku|jêgirtin=Li gor amareke ajanseke selametiya xorakê, xelkê '''Fransa''' salane nêzîkî 4,000 ton Beqan dixwin, ku ew jî bi qasî goştê 40 nehengên şîn e. Ji bo dabînkirina pêwîstiya bazarê jî, li '''Fransayê''' kêlgeh û cihên taybet ên xwedîkirina Beqan hene. Beşek ji wêneyên di vê wênenûçeyê de, kêlgeha xwedîkirina Beqan li navçeyeke '''Fransayê''' ye, herwiha beşek ji wêneyan jî awayê patin û çêkirina xwarinê ji goştê Beqan di xwaringehan de û pêşkêşkirina wan bi mêvanan nîşan didin.|nivîskar=ciwan ikbal|sernav=Fransî di salekê de 4,000 ton Beqan dixwin|url=https://www.rudaw.net/kurmanci/world/101020201|dîrok=10.10.2020}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Bale awayeke dansê ye. Babetek ji şanoya musîkal e. Ji bo dansa baleyê, divê meriv cansivik û nerm be. Loma jî bale gelekî antremanê dixwaze û divê meriv di temenekî biçûk de dest pê bike. Di baleyê de, meriv li gor livên diyarkirî dansê dike. Hinek ji wan tu di wêne de dibînî. Ji ber ku bale li '''Fransayê''' bi pêş ketiye, loma navên livên wê jî bi [[fransî]] ne.|amadekar=Komxebat|sernav=Ansîklopediya Zarokan|cih=Denmark<br>|weşanxane={{w|Weşanên APECl|APEC}}|sal=2004|rûpel=25|isbn=91-89675-32-0}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala-weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani|weşanxane=BRILL|ISBN=9789004754430|rûpel=383}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala-weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji %&nbsp;70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala-weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}} #* {{jêder-malper/ceribandin|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|tarîx=2021-05-04}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|sal=2013|weşanxane=Nûbihar|rûpel=189|isbn= 9786055402884, 6055402882}} #* {{jêder-malper/ceribandin|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|tarîx=2025-02-02|jêgirtin=adfasdf}} #* {{jêder-kitêb/ceribandin|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}} # Maneya 3an. #* {{quote-web|ckb|author=author|last=last|first=first|authorlink=authorlink|author2=author2|last2=last2|first2=first2|authorlink2=authorlink2|author3=author3|last3=last3|first3=first3|authorlink3=authorlink3|author4=author4|last4=last4|first4=first4|authorlink4=authorlink4|author5=author5|last5=last5|first5=first5|authorlink5=authorlink5|coauthors=coauthors|quotee=quotee|translator=translator|translators=translators|title=title|trans-title=trans-title|work=work|trans-work=trans-work|url=url|archiveurl=archiveurl|archivedate=archivedate|format=format|worklang=worklang|location=location|publisher=publisher|month=month|year=year|year_published=year_published|date=date|accessdate=accessdate|nodate=nodate|page=page|pages=pages|pageurl=pageurl|section=section|sectionurl=sectionurl|text=text|passage=passage|lang=lang|brackets=brackets|t=t|translation=translation|lit=lit|tr=tr|transliteration=transliteration|subst=subst}} #* {{quote-book|1=1|author=author|authorlink=authorlink|chapter=chapter|editor=editor|editors=editors|title=title|url=url|archiveurl=archiveurl|archivedate=archivedate|edition=edition|location=location|publisher=publisher|date=date|isbn=isbn|oclc=oclc|volume=volume|section=section|sectionurl=sectionurl|page=page|pages=pages|pageurl=pageurl|text=text|t=t|tr=}} #* {{quote-journal|1=1|year=year|author=author|title=title|journal=journal|url=url|volume=volume|issue=issue|page=page|text=text|t=}} auwzzn8fpe8uk67r4zi0awdlj5va08b 6291567 6291565 2026-07-26T14:59:23Z Wikihez 7344 kurdîkirina şablonên jêderê (citeKurdifier) 6291567 wikitext text/x-wiki {{/vê rêzê neguhêre}} == {{ziman|en}} == === Lêker === [[Wêne:Example.jpg|thumb|Mînak]] {{lêker|en}} # Maneya 1ê. #: {{mk|ku|'''Nimûneyeke''' bikaranînê ya vê peyvê.}} #* {{jêder-kovar/ceribandin|en|nivîskar=Justin Chang|lînka-nivîskar=Justin Chang|sernav=The Day He Arrives [film review]|url=https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|urlya-arşîvê=https://web.archive.org/web/20170628015008/https://variety.com/2011/film/markets-festivals/the-day-he-arrives-1117945326/|tarîxa-arşîvê=28 June 2017|kovar=[[w:Variety (magazine)|Variety]]|tarîx=25 May 2011|issn=0042-2738|oclc=960911146|jêgirtin=The outline could apply to most of the self-effacing, increasingly inward-looking comedies written and directed by [[w:Hong Sang-soo|Hong Sang-soo]], and it certainly describes "[[w:The Day He Arrives|The Day He Arrives]]," an agreeably meandering exercise that brings some clever French New Wave '''fillips''' and structural repetitions to Hong's characteristically boozy party.}} #* {{jêder-kitêb/ceribandin|en|nivîskar=F[rancis] Scott Fitzgerald|lînka-nivîskar=F. Scott Fitzgerald|beş=IX|sernav=[[w:The Great Gatsby|The Great Gatsby]]|cih=New York, N.Y.|weşanxane=[[w:Charles Scribner's Sons|Charles Scribner’s Sons]]|sal=1925|oclc=884653065|cih2=New York, N.Y.|weşanxane2=Charles Scribner’s Sons|sal2=1953|isbn2=978-0-684-16498-4|rûpel2=182|urlya-rûpelê2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|jêgirtin=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.}} #* {{jêder-malper/ceribandin|en|nivîskar=Meg Downey|sernav=Marvel’s Comic Soul Stone could Explain the Jump from Infinity War’s Ending to Avengers 4: A Brief History of Crazy Multidimensional Power|url=https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|urlya-arşîvê=https://web.archive.org/web/20180624110542/https://www.polygon.com/2018/5/7/17328230/infinity-war-avengers-4-marvel-comics-soul-stone|tarîxa-arşîvê=24 June 2018|xebat=[[w:Polygon (website)|Polygon]]|tarîx=7 May 2018|jêgirtin=There’s a very real chance that, rather than crumbling into the dust and floating off into the '''ether''', Thanos’s victims [in the film ''[[w:Avengers: Infinity War|Avengers: Infinity War]]''] were actually sucked up into the Soul Stone.}} #* {{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=The Republic of Molossia has played a considerable role in developing '''intermicronational''' diplomacy.}} #* {{jêder-kitêb/ceribandin|en |sal=c. 1988|nivîskar=Peter W. Rodino, Jr.; et al.|sernav=Report of the Congressional Committees Investigating the Iran/Contra Affair|urlya-rûpelê=http://books.google.com/books?id=ew_K3auTwEgC&pg=PA646&dq=phone+call|rûpel=646|jêgirtin=On November 20, they had a '''phone call''' and later a meeting with others lasting at least 1 1/2 hours.}} #* {{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=Centrally acting '''sympathicolytics''' cause a decrease in the sympathetic outflow from the brainstem. This reduction in sympathetic outflow brings about vasodilatation of the peripheral vascular bed with a concomitant drop in the blood pressure}} #* {{jêder-kitêb/ceribandin|en |sal=2005|edîtor=Martin Endress; George Psathas; Hisashi Nasu|beş=Between the Everyday Life-World and the World of Social Scientific Theory—Towards an “Adequate” Social Thory|nivîskar=Hisashi Nasu|sernav=Explorations of the Life-World: Continuing Dialogues with Alfred Schutz|weşanxane=Springer|isbn=1402032196|rûpel=136|jêgirtin=It is not only the '''man on the street''' who is characterized by taking one's own system of relevance for granted. This holds true for the expert,....}} #* {{jêder-kitêb/ceribandin|en |sal=2002|nivîskar=Peter Shirlow|beş=Sinn Féin: Beyond; within Containment|hejmara-beşê=4|edîtor=Stefan Wolff; Jörg Neuheiser|sernav=Peace at Last?: The Impact of the Good Friday Agreement on Northern Ireland|weşanxane=Berghahn Books|isbn=157181518X|urlya-rûpelê=http://books.google.com/books?id=QyInASKCFjcC&pg=PA62&dq=ethnosectarian&sig=Q54T1Vz3vsKnqd05666N1ddFki8|rûpel=62|jêgirtin=Moreover, language, discourse, and dialogue, their definition and mobilisation, create chains of equivalence which enclose subjects within not only '''ethnosectarian''' but at times entirely self-referential and self-sustained notions of identity, practice and collective devotion.}} #* {{jêder-kitêb/ceribandin|en |sernav=Extreme Hydrological Events: New Concepts for Security|edîtor=O.F. Vasiliev; P.H.A.J.M. van Gelder; E.J. Plate; M.V. Bolgov|sal=2007|weşanxane=[[w:Springer Science+Business Media|Springer]]|isbn=1402057393|beş=Predetermination of Floods|nivîskar=Pierre Hubert; Ioulia Tchiguirinskaia; Daniel Schertzer; Hocine Bendjoudi; Shaun Lovejoy|qisim=On Probability and Return Periods of Floods|hejmara-qismê=2|rûpel=187|jêgirtin=It is perfectly legitimate to be interested, particularly for safety studies, by millennium floods or floods or '''decamillennium''' floods (i.e. having a probability 0,001 and 0,0001 respectively to occur in the course of given year) of a river which did not exist five thousand years ago and that will not exist perhaps any more in ten thousand years, similarly to the industrial interest in the probability of faults which will not undoubtedly occur, because the minimization of their probability is an aspect of a policy of industrial security.}} #* {{jêder-kitêb/ceribandin|en |sal=1995|nivîskar=w:Robert M. Solovay|nivîskarê-sereke=w:Kurt Gödel|edîtor=w:Solomon Feferman; w:John W. Dawson Jr.; w:Warren Goldfarb; {{w|Charles Parsons (philosopher)|Charles Parsons}}; w:Robert M. Solovay|beş=Notes to *1970 &#91;{{w|Godel's ontological proof}}&#93;|sernav=Kurt Gödel: Collected Works: Volume III|urlya-rûpelê=https://books.google.com.au/books?id=gDzbuUwma5MC&pg=PA419&dq=%22Hausdorff+gap%22%7C%22Hausdorff+gaps%22&hl=en&newbks=1&newbks_redir=0&sa=X&ved=2ahUKEwjo-o7D9OT7AhVQJUQIHaSlBIgQ6AF6BAhXEAI#v=onepage&q=%22Hausdorff%20gap%22%7C%22Hausdorff%20gaps%22&f=false|rûpel=419|weşanxane=w:Oxford University Press|jêgirtin=Of course, Hausdorff did not talk of models of set theory or prove absoluteness results. He gave a direct construction in ''ZFC'' of what is now called a '''Hausdorff gap''' and proved properties 1 through 4 for his construction. The notion of a '''Hausdorff gap''' and the proof that '''Hausdorff gaps''' are indestructible under cardinal preserving extensions (property 5 above) are due to Kunen (unpublished).}} #* {{jêder-kitêb/ceribandin|en |sal=1925|nivîskar={{w|F. Scott Fitzgerald|F[rancis] Scott Fitzgerald}}|beş=IX|sernav=w:The Great Gatsby|cih=New York, N.Y.|weşanxane=w:Charles Scribner's Sons|oclc=884653065|sal2=1953|cih2=New York, N.Y.|weşanxane2=Charles Scribner’s Sons|isbn2=978-0-684-16498-4|rûpel2=182|urlya-rûpelê2=https://archive.org/stream/in.ernet.dli.2015.184960/2015.184960.The-Great-Gatsby#page/n185/mode/1up|jêgirtin=So we beat on, boats against the current, borne back '''ceaselessly''' into the past.|lccn=gm 95-1}} #* {{jêder-kitêb/ceribandin|en |nivîskar=w:John Boyle O'Reilly|beş=On the Trail|sernav=[[w:Moondyne|Moondyne: A Story from the Under-world]]|cih=London|weşanxane=[[w:Routledge|George Routledge and Sons]]|sal=1878|sala-weşandinê=1879|oclc=39983928|sernav2=Moondyne: A Story of Convict Life in Australia|cih2=London|weşanxane2=George Routledge & Sons, Limited, Broadway House, {{w|Ludgate Hill}}|sal2=[1880s]|qisim2=book first|pages2=23–24|urlya-rûpelê2=https://archive.org/stream/moondynestory00oreirich#page/24/mode/1up/|oclc2=83033698|jêgirtin=It was sore travelling for horse and man under the blazing sun, with no food nor water save what he pressed from the pith of the palms, and even these were growing scarce. The only life on the plains was the hard and dusty scrub. Every hour brought a more hopeless and '''grislier''' desolation.}} #* {{jêder-kitêb/ceribandin|en |sal=1966|sala-orîjînal=1858|nivîskar=w:Hans Christian Andersen|terciman=Pat Shaw Iversen|orîjînal=Suppe paa en Pølsepind|zimanê-orîjînal=da|beş=Soup from a Sausage Peg|sernav=The Snow Queen and Other Tales|serî=Signet Classic|cildê-seriyê=CT334|cih=New York, N.Y.|weşanxane=w:New American Library|rûpel=224|urlya-rûpelê=http://books.google.com/books?id=2tjWAAAAMAAJ&q=%22from+your+own+nook+and+cranny%22#search_anchor|oclc=636818779|metna-orîjînal=da:Det er underligt at komme hjemme fra sin egen '''Krinkelkrog''', gaae med Skib, der ogsaa er en Slags '''Krinkelkrog''', og saa pludselig være over hundrede Mile borte og staae i et fremmedt Land.|jêgirtin=It’s strange to come away from home, from your own '''nook and cranny''', to go by ship – which is also a kind of '''nook and cranny''' – and then suddenly be more than a hundred miles away and stand in a foreign land!}} #* {{jêder-kitêb/ceribandin|en |sal=2013|nivîskar=Bernd Klauer; Reiner Manstetten; Thomas Petersen; Johannes Schiller|zimanê-eserê=de|beş=Materielle Bestände|sernav=Die Kunst, langfristig zu denken : Wege zur Nachhaltigkeit<t:The art of thinking long-term : paths to sustainability>|cih=Baden-Baden|weşanxane=Nomos|versiyona-nû=translated as|terciman2=Kathleen Cross|sal2=2016|sernav2=Sustainability and the Art of Long-Term Thinking|beş2=Material stocks|cih2=London; New York|weşanxane2=Routledge, Taylor & Francis Group|qisim2=4.3.1. Material sets and stocks|rûpel2=44|isbn2=9781134986187|jêgirtin=The property of '''appertainment''' of a set is therefore the property which, by definition, has to be given in every element of the set.}} #* {{jêder-kitêb/ceribandin|en |nivîskar={{w|Andrew Seth Pringle-Pattison|Andrew Seth}}|sernav=Hegelianism and Personality|çap=2nd|sal=1887|sala-weşandinê=1893|rûpel=216|rêz-sade=footnote|cih=Edinburgh; London|weşanxane=William Blackwood and Sons|versiyona-nû=quoted in|sal2=1893|2nivîskar=w:David George Ritchie|sernav2=Darwin and Hegel, with other Philosophical Studies|cih2=London|weşanxane2=S. Sonnenschein & Co.; New York, N.Y.: [[w:Macmillan Publishers (United States)|Macmillan & Co.]]|rûpel2=72|urlya-rûpelê2=http://books.google.com/books?id=8yGEAHvAwiIC&q=%22An+absolute+system+cannot+afford+to+leave+any+nook+or+cranny+of+existence+unexplored%22#search_anchor|rêz-sade2=note 1|oclc2=3299658|jêgirtin=An absolute system cannot afford to leave '''any nook or cranny''' of existence unexplored.}} #* {{jêder-kitêb/ceribandin|en |nivîskar=w:David George Ritchie|jê-tê-girtin=w:Andrew Seth Pringle-Pattison|sernav=Hegelianism and Personality|çap=2nd|kesên-din=1893, page 216, footnote|cihê-jêgirtinê=Darwin and Hegel, with other Philosophical Studies|cih=London|weşanxane=S. Sonnenschein & Co.; New York, N.Y.: [[w:Macmillan Publishers (United States)|Macmillan & Co.]]|sal=1893|rûpel=72|rêz-sade=note 1|urlya-rûpelê=http://books.google.com/books?id=8yGEAHvAwiIC&q=%22An+absolute+system+cannot+afford+to+leave+any+nook+or+cranny+of+existence+unexplored%22#search_anchor|oclc=3299658|jêgirtin=An absolute system cannot afford to leave '''any nook or cranny''' of existence unexplored.}} #* {{jêder-kovar/ceribandin|en |nivîskar=w:Hayden Carruth|sernav=Making It New|kovar=w:The Hudson Review|cih=New York, N.Y.|weşanxane=Hudson Review, Inc.|meh=Summer|sal=1968|cild=XXI|hejmar=2|issn=2325-5935|oclc=920393805|versiyona-nû=reprinted as|cure2=book|beş2=From ‘Making It New’ [''Body Rags'']|edîtor2=Howard Nelson|sernav2=On the Poetry of Galway Kinnell: The Wages of Dying|serî2=Under Discussion|cih2=Ann Arbor, Mich.|weşanxane2={{w|University of Michigan Press}}|sal2=1987|rûpel2=75|urlya-rûpelê2=https://books.google.com/books?id=7cXV26PmJUYC&pg=PA75|isbn2=978-0-472-09376-2|jêgirtin=In his &#91;{{w|Galway Kinnell}}'s&#93; new book, ''Body Rags'', he has brought this style to a kind of perfection, especially in two poems about the killing of animals, "The Porcupine" and "The Bear." These are the '''grisliest''' poems I have ever read.}} #* {{jêder-kitêb/ceribandin|fr |sal=1973|nivîskar=w:Claude Simon|sernav=Tryptique|weşanxane=Éditions de Minuit|rûpel=12|jêgirtin=Les sons de la cloche '''égrenant''' les quarts, les demies et les heures {{...}}|t=The sounds of the clock '''rattling off''' the quarters, the halves and the hours {{...}}}} #* {{jêder-kitêb/ceribandin|de |sal=1999|nivîskar=Paul Hoffmann|hevnivîskar=Stefan H. Brandenburger; Ulrike Brauner; Thomas Hieke|sernav=Q 22:28, 30: You Will Judge the Twelve Tribes of Israel|serî=Documenta Q: Reconstructions of Q Through Two Centuries of Gospel Research Excerpted, Sorted and Evaluated|cildê-seriyê=volume 5|isbn=90-429-0734-7|rûpel=189|jêgirtin=Zumal in beiden Fällen eine Partizipialform vorliegt, liegt es nahe, die '''Zeitworte''' als Varianten anzusehen.}} #* {{jêder-kitêb/ceribandin|ru |nivîskar=lw:ru:[[Филарет (Дроздов)|Митрополи́т Филаре́т]]<tr:-><t:Mitropolit Filaret>|beş=ru:Сло́во предъ приведеніемъ къ присягѣ Санктпетербурксаго дворя́нства и гражданъ для избранія суде́й|tarîx=1818-02-01<!--Converted to Gregorian-->|sala-weşandinê=1873|cih=Moscow|sernav=ru:Сочинеиія Филарета Митрополита Московскаго и Коломенскаго|cild=I|rûpel=253|urlya-rûpelê=https://books.google.com/books?id=yWEcAAAAMAAJ&pg=PA253|jêgirtin=[[изъ|Изъ]] [[сей|сего́]] [[ви́дно]], [[что]] [[судья|судія́]], [[как|какъ]] [[блюсти́тель]] [[общественный|обще́ственнаго]] [[порядок|поря́дка]], [[должен|долженъ]] '''объима́ть''' [[и]] [[проника́ть]] [[мысль|мы́слію]] [[весь]] [[состав|соста́въ]] [[общество|о́бщества]];|t=From this it becomes discernible that a judge, in his function as a warden of public order, should '''enclose''' and traverse the whole body of the society;}} #* {{jêder-kitêb/ceribandin|yi |nivîskar=I.J. Singer|sernav=Fun a velt vos iz nishto mer|sal=1946|beş=yi:אַ ייד אַ מלמד פֿאַרגלוסט זיך צו ווערן אַ מלאך אום פּורים און פֿאַרלירט, נעבעך, אַן אויג<subst:ייד/ייִד,מלמד/מעלאַמעד,פּורים/פּורעם,מלאך/מאַלעך><t:A Jew, a teacher, decides to become an angel on Purim and, unfortunately, loses an eye>|jêgirtin=יעדן '''זמן''' אויפֿסניי, חול־המועד סוכות אָדער חול־המועד פּסח, בין איך געווען זייער האָפֿערדיק, וואָס מען האָט מיך גענומען איין מלמד און אָנגעדונגען צו אַן אַנדערן, גלײבנדיק, אַז דער נייער וועט זיין בעסער ווי דער אַלטער.|subst=זמן//זמאַן,חול־המועד//כאַלעמויעד,סוכות//סוקוס,פּסח//פּייסעך,מלמד//מעלאַמעד,נייער//נײַער|tercime=Every '''term''' anew, during the intermediary periods of Sukkot and Pesach, I was very confident in being taken from one melamed and given to another, believing the new one would be better than the old.}} #* {{jêder-kitêb/ceribandin|ru |sal=1983|nivîskar=w:Samuil Marshak|sernav=Баллада о королевском бутерброде<subst:бутер/бутэр><t:Ballad of the King's Bread>|orîjînal=The King's Breakfast|ji-aliyê=w:A. A. Milne|metna-orîjînal=en:"Nobody," / He whimpered, / "Could call me / A fussy man; / I only want / A little bit / Of butter for / My bread!"|zimanê-orîjînal=en|jêgirtin=— [[никто|Никто́]] [[не]] [[сказать|ска́жет]], [[бу́дто]] [[я]] / [[тиран|Тира́н]] [[и]] [[сумасбро́д]], / [[за|За]] [[то́]], [[что]] [[к]] [[чай|ча́ю]] [[я]] [[любить|люблю́]] / [[хороший|Хоро́ший]] '''бутербро́д'''.|subst=бутербро́д/бутэрбро́д|t="No one would say that I / Am a tyrant and madman, / For the fact that with my tea I like / A good '''bread-and-butter sandwich'''."}} #* {{jêder-kitêb/ceribandin|ota |sal=1922|sernav=ota:ادارهٔ حرب و سیاست<tr:Idare-i harb ve siyaset>|cih=Istanbul|weşanxane=ota:مطبعهٔ عثمانیه|rûpel=176|urlya-rûpelê=https://babel.hathitrust.org/cgi/pt?id=uc1.aa0006749931&view=1up&seq=182|versiyona-nû=translation of|ziman2=de|sal2=1922|2nivîskar=w:Erich Ludendorff|sernav2=Kriegsführung und Politik|cih2=Berlin|weşanxane2=E. S. Mittler & Sohn|rûpel2=199|urlya-rûpelê2=https://archive.org/details/kriegfhrungund00lude/page/199|jêgirtin=شرقی غالیچیا، و بوقوونیاده شمندوفر نقاباتی تأسس ایتدكدن صكره شرقده '''صلحی''' تأمین ایچون دینیه‌ستر نهری جنوبندن روس — رومن جبهه‌سنه قارشو حربه دوام ایتمك فكرندن فراغت ایتمك لازمكلیورایدی. بر قصقاچ تأثیری یاپه‌بیلمك ایچون بری قلاص غربندن یوقاره‌سه‌ره‌ت اوزرینه و دیكری دینیه‌ستر جنوبندن اولمق اوزره مضاعف بر تعرض اجراسی لازمكلیور ایده.|tr=Şarki Galiçya, ve Bukovinyada‌ şömendüfer nakabatı tesis itdikden soñra şarkda sulhı temin içün Dinyester nehri cenubundan rus-rumen cebhesine karşu harbe devam itmek fikrinden feragat itmek lazım geliyor idi. Bir kıskaç tesiri yapabilmek içün biri Kalas garbından yukara-yı Seret üzerine ve diğeri Dinyester cenubundan olmak üzere muzaaf bir taarruz icrası lazım geliyor idi.|metna-orîjînal=de:Unsere weitere Absicht, nach Herstellung der Eisenbahnverbindungen in Ostgalizien und in der Bukowina zur Erzwingung des Friedens im Osten den Feldzug gegen die russisch-rumänische Front südlich des Dnjestr fortzusetzen, mußte aufgegeben werden. Er sollte in einem doppelten Angriff über den oberen Sereth westlich Galatz und hart südlich des Dnjestr bestehen und auch zangenartig wirken.|t=Our further intention, the military campaign against the Russian-Romanian front to be continued south of the Dniestr to force '''peace''' in the east, after procurement of the railroad connections in Eastern Galicia and in the Bukovina, had to be abandoned. It was supposed to consist in a double attack over the upper Siret west of Galați as well as hard south of the Dniestr and go like pincers.}} #* {{jêder-kitêb/ceribandin|it |sernav=L'œuvre complète de Tchouang-tseu|terciman=Liou Kia-hway<f:Hant:劉家槐><f:Pinyin:Liú Jiāhuái>|orîjînal={{w|Zhuangzi (book)|Zhuang-zi}}|ji-aliyê=莊周<t:Zhuang Zhou>|zimanê-orîjînal=och|sala-orîjînal=c. 300 {{BCE}}|sal=1969|sal2=1982|sala-weşandinê2=2010|versiyona-nû=translated as|terciman2=Carlo Laurenti; Christine Leverd|sernav2=Zhuang-zi [Chuang-tzu]|serî2=Gli Adelphi|cildê-seriyê2=41|beş2=Libertà naturale|tercimeya-beşê2=Natural Freedom|çap2=6th|cih2=Milan|weşanxane2={{w|Adelphi Edizioni}}|rûpel2=17|isbn2=978-88-459-0950-4|metna-orîjînal=och:之人也,物莫之傷,大浸稽天而不溺,大旱、金石流、土山焦而不熱。|jêgirtin=Se le acque si alzassero fino al cielo, non annegherebbero. Se la siccità '''liquefacesse''' i metalli e infiammasse le montagne, non ne sarebbero neppure sfiorati.|t=If the waters were to rise to the skies, they wouldn't drown. If drought were to '''liquefy''' metals, and set fire to the mountains, they wouldn't even be touched by it.}} #* {{jêder-kitêb/ceribandin|fr |zimanê-termê=en|sal=1677|nivîskar=Guy Miege [''i.e.'', [[w:fr:Guy Miège|Guy Miège]]]|made=HAGGESS|urlya-madeyê=https://books.google.com/books?id=YbWtTwVbLioC&pg=PP857|sernav=A New Dictionary French and English, with Another English and French; According to the Present Use, and Modern Orthography of the French. Inrich’d with New Words, Choice Phrases, and Apposite Proverbs; Digested into a Most Accurate Method; and Contrived for the Use Both of English and Foreiners|cih=London|weşanxane=Printed by Tho[mas] Dawks, for Thomas Basset, at the George, near [[w:Clifford's Inn|Cliffords-inn]], in [[w:Fleet Street|Fleetstreet]]|stûn=3|oclc=874955677|jêgirtin='''HAGGESS''', '''haggas''', or '''haggis''', ſort d'appret fait avec des herbes, du larde caupé menu, des épices, des œufs, & du fromage, le tout mis enſemble & bouilli dans une panſe de brebis.|t='''HAGGESS''', '''haggas''', or '''haggis''', a sort of preparation made with herbs, bacon, spices, eggs, cheese, all put together and boiled in a sheep's belly.|brackets=1}} #*{{jêder-kovar/ceribandin|en|sernav=Art VIII.—Who are the True Conservatives?|kovar=The Quarterly Christian Spectator|cih=New Haven, Conn.|weşanxane=Published for the proprietor by Hezekiah Howe [...]|meh=November|sal=1838|cild=X|hejmar=IV|rûpel=608|urlya-rûpelê=https://books.google.com/books?id=S7MAAAAAMAAJ&pg=PA608|oclc=5090641|jêgirtin=They have guarded a few minds against '''sciolism''' and pretension in philosophy, and they have given a new and more imposing air to '''sciolism''' itself, and placed in the hands of the charlatan enchantments and magical arts, which hold fast its victims with a potency never granted to an inferior spell. There is no '''sciolism''' now which is more dangerous than that which is so very careful to inform us of its entire freedom from all that is superficial, {{...}}}} #*{{jêder-kovar/ceribandin|en |tarîx=March 8, 2023|paşnav=Wu|pêşnav=Huizhong|nivîskar2=Johnson Lai|sernav=Taiwan suspects Chinese ships cut islands’ internet cables|kovar={{w|The Washington Post}}|urlya-arşîvê=https://web.archive.org/web/20230311220100/https://www.washingtonpost.com/business/2023/03/08/matsu-taiwan-internet-cables-cut-china/d322fcae-bd70-11ed-9350-7c5fccd598ad_story.html|tarîxa-arşîvê=11 March 2023|issn=0190-8286|oclc=2269358|qisim=Business|tarîx2=April 18, 2023|sernav2=Chinese ships cut internet of Taiwan’s outlying islands|cure2=journal|kovar2={{w|AP News}}|urlya-arşîvê2=https://web.archive.org/web/20230308053048/https://apnews.com/article/matsu-taiwan-internet-cables-cut-china-65f10f5f73a346fa788436366d7a7c70|jêgirtin=Wang Chung Ming, the head of '''Lienchiang''' County, as the Matsu islands are officially called, said he and the legislator from Matsu went to Taipei shortly after the internet broke down to ask for help, and was told they would get priority in any future internet backup plans.{{...}}<br>Wang, the head of '''Lienchiang''' County, said he had mentioned the cables on a recent visit to China, where he had met an executive from China Mobile.}} #*{{jêder-kovar/ceribandin|en |sal=1994|meh=April|kovar=光華|tercimeya-kovar=Sinorama|url=https://books.google.com/books?id=GIRDAAAAYAAJ|cild=19|hejmar=1-6|weşanxane={{w|Government Information Office}}|issn=0256-9043|oclc=20549278|sal2=1994|meh2=April|2nivîskar=Yuan-ching Cheng|cure2=journal|sernav2=Kinmen- -From Battlefield to Tourist Destination|kovar2=台灣光華雜誌<t:Taiwan Panorama>|terciman2=Robert Taylor|urlya-arşîvê2=https://web.archive.org/web/20221021103917/https://www.taiwan-panorama.com/en/Articles/Details?Guid=c9335c12-7ec3-40e7-96be-33a81416a223|tarîxa-arşîvê2=21 October 2022|qisim2=Around Taiwan|jêgirtin=The temple to Colonel Li Kuang-chien lies near Hsiputou village in the northwest of Kinmen Island, around five kilometers from the town of '''Chincheng''' and next to the famous Hill 132.{{...}}<br>Because Kinmen was settled early, it bears many traces of the past. There are 21 listed historical monuments scattered all over the island. The only Grade I monument among them is the "Chiehhsiao Arch," commemorating Chiu Liang-kung's mother, located in East Houpu Street in the center of '''Chincheng'''.}} == {{ziman|ku}} == === Lêker === [[Wêne:Example.jpg|thumb|Mînak]] {{lêker|ku}} # [[testsdfsdafdasfas]] #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Mîr Kamûran di sala 1947 an de li '''Fransayê''' bicîh bû, û heta 1970ê li Xwendegeha zimanên rojhilatî ya Zaningeha Parîsê Profesorîya ziman û çanda Kurdî kir.|nivîskar=Kamuran A.Bedir Khan|nivîskar2=Josefe Bertolino|nivîskar3=Kendal Nezan|sernav=Institut Kurde De Paris Ferhenga Kurdî-Fransizî|cih=Istanbul|weşanxane={{w|Weşanên Avesta|Avesta}}|sal=2017|çap=1|rûpel=XXXIX|isbn=978-2-360134311}} #* {{jêder-malper/ceribandin|ku|jêgirtin=Li gor amareke ajanseke selametiya xorakê, xelkê '''Fransa''' salane nêzîkî 4,000 ton Beqan dixwin, ku ew jî bi qasî goştê 40 nehengên şîn e. Ji bo dabînkirina pêwîstiya bazarê jî, li '''Fransayê''' kêlgeh û cihên taybet ên xwedîkirina Beqan hene. Beşek ji wêneyên di vê wênenûçeyê de, kêlgeha xwedîkirina Beqan li navçeyeke '''Fransayê''' ye, herwiha beşek ji wêneyan jî awayê patin û çêkirina xwarinê ji goştê Beqan di xwaringehan de û pêşkêşkirina wan bi mêvanan nîşan didin.|nivîskar=ciwan ikbal|sernav=Fransî di salekê de 4,000 ton Beqan dixwin|url=https://www.rudaw.net/kurmanci/world/101020201|dîrok=10.10.2020}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Bale awayeke dansê ye. Babetek ji şanoya musîkal e. Ji bo dansa baleyê, divê meriv cansivik û nerm be. Loma jî bale gelekî antremanê dixwaze û divê meriv di temenekî biçûk de dest pê bike. Di baleyê de, meriv li gor livên diyarkirî dansê dike. Hinek ji wan tu di wêne de dibînî. Ji ber ku bale li '''Fransayê''' bi pêş ketiye, loma navên livên wê jî bi [[fransî]] ne.|amadekar=Komxebat|sernav=Ansîklopediya Zarokan|cih=Denmark<br>|weşanxane={{w|Weşanên APECl|APEC}}|sal=2004|rûpel=25|isbn=91-89675-32-0}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Hekayêta Betal Kirina '''Xezeta''' Hetawî Kurd|sal=~1910|sala-weşandinê=2026|nivîskar=Mela Seîdê Şemdînanî|amadekar=David N. MacKenzie, Ergin Öpengin|url=http://books.google.com.tr/books?id=eqvrEQAAQBAJ&pg=PA383&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=2&source=gbs_api|sernav=Shamdinani Stories I: Kurdish Writings on the History and Culture of Central Kurdistan by Mullah Saʼid Shamdinani|weşanxane=BRILL|ISBN=9789004754430|rûpel=383}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Baş e, '''xezete''' dê bi çi ezmanî derkevit?|nivîskar=E. Rehmî Ji Mala Hekarîyan|amadekar=M. E. Bozarslan|sernav=Jîn: kovara Kurdî-Tirkî. Cild 1|weşanxane=Weşanxana Deng|cih=Uppsala|sala-weşandinê=1985|sal=1918|rûpel=310|isbn=978-91-970702-0-1}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=ligor{{sic}} danezanên '''xezete''' ên Turk hezar insan li kurdistan hatin kuştin ku ji %&nbsp;70 jin û zaruk kal » pîrbûn|nivîskar=Hesen Hişyar Serdî|sernav=Dîtin û Bîrhatinên Min|weşanxane=Çapxana Emîral|cih=Beyrût|sal=1991|sala-weşandinê=2000|rûpel=380|url=https://bnk.institutkurde.org/images/pdf/CTNPMX4FR6.pdf}} #* {{jêder-malper/ceribandin|ku|jêgirtin='''Xezeteyên''' Kurdî yên li Stenbolê derdiketin bên îstisnakirin, xezeteyekî millî û wetenî yê ku yekemîn car li Kurdistanê hatîye neşirkirin, ew jî xezeteya Zarê Kirmancî ye, ku min xwendîye.|nivîskar=Seîd Veroj|sernav=Mesûd Fanî (Dr. Mesûd Fanî Bîlgîlî)|websîte=Rûpela nû|url=https://www.rupelanu.com/seid-veroj-mesud-fani-dr-mesud-fani-bilgili-17258h.htm|tarîx=2021-05-04}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=me '''xezete''' da destê wî jî.|sal=2022|nivîskar=Abdurrahman Benek|url=http://books.google.com.tr/books?id=SdSUEAAAQBAJ&pg=PA33&dq=%22wan+xezetan%22+OR+%22xezet%C3%AA%22+OR+%22xezetin%22+OR+%22xezetek%C3%AA%22+OR+%22xezeta%22+OR+%22w%C3%AA+xezet%C3%AA%22+OR+%22xezetino%22+OR+%22xezetine%22&hl=&cd=3&source=gbs_api|sernav=Payiza Mezinan|weşanxane=Nûbihar|ISBN=9786059413749|rûpel=33}} #* {{jêder-kitêb/ceribandin|ku|jêgirtin=Jêgirtinek ji kitêbekê.|nivîskar=Rojen Barnas|sernav=Sîlvan/ Silîvan / FARQÎN|sal=2013|weşanxane=Nûbihar|rûpel=189|isbn=9786055402884, 6055402882}} #* {{jêder-malper/ceribandin|ku|nivîskar=Gazete Duvar|sernav=Şairê dubeytan: Baba Tahirê Uryan|websîte=https://www.gazeteduvar.com.tr|url=https://www.gazeteduvar.com.tr/saire-dubeytan-baba-tahire-uryan-haber-1753893|tarîx=2025-02-02|jêgirtin=adfasdf}} #* {{jêder-kitêb/ceribandin|ku|sernav=Shamdinani Stories I|weşanxane=Brill|sal=2026|isbn=978-90-04-75443-0|url=https://brill.com/edcollbook-oa/title/74006|jêgirtin=test}} # Maneya 3an. #* {{jêder-malper/ceribandin|ckb|nivîskar=author|paşnav=last|pêşnav=first|lînka-nivîskar=authorlink|nivîskar2=author2|paşnav2=last2|pêşnav2=first2|lînka-nivîskar2=authorlink2|nivîskar3=author3|paşnav3=last3|pêşnav3=first3|lînka-nivîskar3=authorlink3|nivîskar4=author4|paşnav4=last4|pêşnav4=first4|lînka-nivîskar4=authorlink4|nivîskar5=author5|paşnav5=last5|pêşnav5=first5|lînka-nivîskar5=authorlink5|hevnivîskar=coauthors|jê-tê-girtin=quotee|terciman=translators|sernav=title|tercimeya-sernav=trans-title|xebat=work|trans-work=trans-work|url=url|urlya-arşîvê=archiveurl|tarîxa-arşîvê=archivedate|format=format|zimanê-eserê=fr|cih=location|weşanxane=publisher|sala-weşandinê=year_published|tarîx=2022-01-25|tarîxa-gihiştinê=2022-01-25|rûpel=5|urlya-rûpelê=pageurl|qisim=section|urlya-qismê=sectionurl|jêgirtin=passage|brackets=brackets|t=translation|hrf=lit|tr=transliteration|tr=transliteration|subst=subst}} #* {{jêder-kitêb/ceribandin|ckb|nivîskar=author|lînka-nivîskar=authorlink|beş=chapter|edîtor=editors|sernav=title|url=url|urlya-arşîvê=archiveurl|tarîxa-arşîvê=archivedate|çap=edition|cih=location|weşanxane=publisher|tarîx=2022-05-09|isbn=isbn|oclc=oclc|cild=volume|qisim=section|urlya-qismê=sectionurl|rûpel=page|pages=pages|urlya-rûpelê=pageurl|jêgirtin=text|t=t|tr=}} #* {{jêder-kovar/ceribandin|fr|sal=2025|nivîskar=author|sernav=title|kovar=journal|url=url|cild=volume|hejmar=issue|rûpel=page|jêgirtin=text|t=}} 2uhqd38jbb5gs2ztnk9o3vuwrc5anee Bikarhêner:Wikihez/Gadget-start.css 2 1357891 6291585 6276331 2026-07-26T17:40:09Z Wikihez 7344 6291585 css text/css /* =================================================================== MediaWiki:Gadget-start.js — companion styles Copy this file into MediaWiki:Common.css (or a dedicated gadget CSS page) so that the JS gadget no longer needs to inject styles at runtime. Requires MediaWiki:Gadget-Palette.css to be loaded for the --wikt-palette-* custom properties. =================================================================== */ /* ── Continue/submit button── */ .wiktku-btn { padding: 0.4em 1.2em; font-size: 1em; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 2px; background: var(--wikt-palette-white); cursor: pointer; } .wiktku-btn:hover { border-color: var(--wikt-palette-blue-9, #72777d); } .wiktku-btn:disabled { cursor: default; opacity: 0.5; } .wiktku-btn-progressive { background: var(--wikt-palette-blue-9); border-color: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } .wiktku-btn-progressive:hover:not(:disabled) { filter: brightness(1.1); } .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); } .wiktku-start-step { padding: 1em 0; border-bottom: 1px solid #e0e0e0; } .wiktku-start-step-question { font-size: 125%; } .wiktku-start-step label { padding-right: 2em; } input[type="text"].wiktku-start-step-field { width: 100%; } input[type="text"].wiktku-start-step-field.wiktku-start-narrow { width: 20em; } select.wiktku-start-narrow { width: 20em; max-width: 100%; box-sizing: border-box; } #wiktku-start-spinner { float: right; margin-left: 1em; } #wiktku-start-form textarea.wiktku-start-step-field, #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield { resize: both !important; overflow: auto !important; box-sizing: border-box !important; vertical-align: top; } #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow { width: 20em; min-width: 8em; height: calc(1em + 8px); min-height: calc(1em + 8px); padding-top: 2px !important; padding-bottom: 2px !important; line-height: 1.4 !important; } /* ── Mobile autocomplete widget ── */ .wiktku-native-select-wrap { display: inline-block; position: relative; } .wiktku-native-select-wrap input[type="search"] { display: block; width: 20em; max-width: 100%; box-sizing: border-box; } ul.wiktku-ac-results { position: absolute; z-index: 9999; top: 100%; bottom: auto; left: 0; background: var(--wikt-palette-white); border: 1px solid var(--wikt-palette-lightgrey); margin: 0; padding: 0; list-style: none; width: 20em; max-width: 90vw; max-height: 14em; overflow-y: scroll; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; } ul.wiktku-ac-results li { padding: 6px 10px; cursor: pointer; font-size: 0.95em; min-height: 2.4em; display: flex; align-items: center; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } /* ── Wikitext preview panel ── */ #wiktku-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); white-space: pre-wrap; word-break: break-word; font-family: monospace; font-size: 0.9em; } /* ── Live rendered preview panel (desktop only) ── */ #wiktku-live-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); word-break: break-word; min-height: 2em; } /* ── Preview container (holds wikitext + live panels) ── */ #wiktku-preview-container { display: flex; flex-wrap: wrap; gap: 1em; margin-top: 0.5em; } #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 .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text, #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 2; width: 100%; box-sizing: border-box; height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); min-height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); } #wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 3; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 4; } .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.4 !important; font-size: 0.9em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr { background: var(--wikt-palette-cyan); } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t { background: var(--wikt-palette-palegreen); } /* ── Template selector row inside a sense block ── */ .wiktku-dfn-tpl-row { margin-bottom: 4px; } .wiktku-dfn-tpl-select { font-size: 0.9em; } input.wiktku-dfn-tpl-title { width: 20em; max-width: 100%; box-sizing: border-box; } .wiktku-pron-wrap, .wiktku-etym-wrap { display: flex; flex-direction: column; gap: 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { border: 1px solid var(--wikt-palette-lightgrey); font-size: 0.9em; width: 100%; box-sizing: border-box; padding: 3px 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-amber-1); } .wiktku-etym-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-indigo-1); } .wiktku-pron-wrap textarea.wiktku-pron-text { background: var(--wikt-palette-amber-0); } .wiktku-etym-wrap textarea.wiktku-etym-text { background: var(--wikt-palette-indigo-0); } /* ── POS section tabs ── */ #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 10px; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 3px 3px 0 0; background: var(--wikt-palette-dullcyan); cursor: pointer; font-size: 0.9em; user-select: none; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white); border-bottom-color: var(--wikt-palette-white); font-weight: bold; } .wiktku-lang-row-list { margin: 0; padding: 0; list-style: none; } .wiktku-lang-row { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 4px; flex-wrap: wrap; } .wiktku-lang-row-words { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 3px; } /* ── Werger (translation) groups — one per gloss ── */ #wiktku-start-trans .wiktku-trans-group { margin-bottom: 0.8em; } #wiktku-start-trans .wiktku-trans-group:last-child { margin-bottom: 0; } #wiktku-start-trans .wiktku-trans-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } /* ── Bi zaravayên din (dialect) groups — one per gloss, mirrors Werger ── */ #wiktku-start-zarava .wiktku-zarava-group { margin-bottom: 0.8em; } #wiktku-start-zarava .wiktku-zarava-group:last-child { margin-bottom: 0; } #wiktku-start-zarava .wiktku-zarava-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } .wiktku-dfn-jeder-group { margin-top: 4px; } /* Drag handle for reordering citation rows */ .wiktku-jeder-drag-handle { display: inline-block; cursor: grab; color: var(--wikt-palette-lightgrey, #ccc); font-size: 1.2em; line-height: 1; margin-right: 4px; vertical-align: middle; user-select: none; } .wiktku-jeder-drag-handle:active { cursor: grabbing; } .wiktku-dfn-jeder-list .ui-sortable-placeholder { border: 2px dashed var(--wikt-palette-lightgrey, #ccc); background: transparent; visibility: visible !important; height: 2em; } .wiktku-dfn-quote-src-sel { font-size: 0.9em; margin-top: 4px; display: block; } .wiktku-dfn-quote-src-fields { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; } .wiktku-dfn-quote-src-cat-status { display: block; font-size: 0.85em; 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.85em; white-space: nowrap; min-width: 8em; } input.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; } #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-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 4; font-size: 1em; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 6; font-size: 1em; } #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: 3; font-size: 1em; } .wiktku-dfn-nimune-sub { margin-left: 0.5em; padding-left: 0.4em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t { max-width: calc(100vw - 6em); } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { font-size: 1em; min-height: 2.4em; } .wiktku-dfn-quote-src-row label { min-width: 6em; } input.wiktku-dfn-quote-src-input { width: 100%; flex: 1 1 100%; } } @media (prefers-color-scheme: dark) { ul.wiktku-ac-results { background: var(--wikt-palette-white, #1e1e1e); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9, #3a6ea8); color: #fff; } .wiktku-start-step { border-bottom-color: var(--wikt-palette-lightgrey, #444); } .wiktku-addlang-notice { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-preview-panel, #wiktku-live-preview-panel { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-section-tabs .wiktku-section-tab { background: var(--wikt-palette-dullcyan, #1e2d3a); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white, #121212); border-bottom-color: var(--wikt-palette-white, #121212); } } /* ── "Zimanek lê zêde bike" portlet link icon (Minerva/mobile) ── */ /* Minerva auto-generates the icon span class from the portlet link id. */ /* Uses Feather-core-edit.svg from Wikimedia Commons via CSS mask, matching */ /* the exact pattern Minerva uses for all its own icons. */ .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; } mi0hfvqirb3ighc3bud5diy1ab3hzbn 6291589 6291585 2026-07-26T18:33:01Z Wikihez 7344 6291589 css text/css /* =================================================================== MediaWiki:Gadget-start.js — companion styles Copy this file into MediaWiki:Common.css (or a dedicated gadget CSS page) so that the JS gadget no longer needs to inject styles at runtime. Requires MediaWiki:Gadget-Palette.css to be loaded for the --wikt-palette-* custom properties. =================================================================== */ /* ── Continue/submit button── */ .wiktku-btn { padding: 0.4em 1.2em; font-size: 1em; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 2px; background: var(--wikt-palette-white); cursor: pointer; } .wiktku-btn:hover { border-color: var(--wikt-palette-blue-9, #72777d); } .wiktku-btn:disabled { cursor: default; opacity: 0.5; } .wiktku-btn-progressive { background: var(--wikt-palette-blue-9); border-color: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } .wiktku-btn-progressive:hover:not(:disabled) { filter: brightness(1.1); } .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); } .wiktku-start-step { padding: 1em 0; border-bottom: 1px solid #e0e0e0; } .wiktku-start-step-question { font-size: 125%; } .wiktku-start-step label { padding-right: 2em; } input[type="text"].wiktku-start-step-field { width: 100%; } input[type="text"].wiktku-start-step-field.wiktku-start-narrow { width: 20em; } select.wiktku-start-narrow { 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.6em; } #wiktku-start-form textarea.wiktku-start-step-field, #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield { resize: both !important; overflow: auto !important; box-sizing: border-box !important; vertical-align: top; } #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow { width: 20em; min-width: 8em; height: calc(1em + 8px); min-height: calc(1em + 8px); padding-top: 2px !important; padding-bottom: 2px !important; line-height: 1.4 !important; } /* ── Mobile autocomplete widget ── */ .wiktku-native-select-wrap { display: inline-block; position: relative; } .wiktku-native-select-wrap input[type="search"] { display: block; width: 20em; max-width: 100%; box-sizing: border-box; } ul.wiktku-ac-results { position: absolute; z-index: 9999; top: 100%; bottom: auto; left: 0; background: var(--wikt-palette-white); border: 1px solid var(--wikt-palette-lightgrey); margin: 0; padding: 0; list-style: none; width: 20em; max-width: 90vw; max-height: 14em; overflow-y: scroll; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; } ul.wiktku-ac-results li { padding: 6px 10px; cursor: pointer; font-size: 0.95em; min-height: 2.4em; display: flex; align-items: center; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } /* ── Wikitext preview panel ── */ #wiktku-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); white-space: pre-wrap; word-break: break-word; font-family: monospace; font-size: 0.9em; } /* ── Live rendered preview panel (desktop only) ── */ #wiktku-live-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); word-break: break-word; min-height: 2em; } /* ── Preview container (holds wikitext + live panels) ── */ #wiktku-preview-container { display: flex; flex-wrap: wrap; gap: 1em; margin-top: 0.5em; } #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 .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text, #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 2; width: 100%; box-sizing: border-box; height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); min-height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); } #wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 3; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 4; } .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.4 !important; font-size: 0.9em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr { background: var(--wikt-palette-cyan); } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t { background: var(--wikt-palette-palegreen); } /* ── Template selector row inside a sense block ── */ .wiktku-dfn-tpl-row { margin-bottom: 4px; } .wiktku-dfn-tpl-select { font-size: 0.9em; } input.wiktku-dfn-tpl-title { width: 20em; max-width: 100%; box-sizing: border-box; } .wiktku-pron-wrap, .wiktku-etym-wrap { display: flex; flex-direction: column; gap: 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { border: 1px solid var(--wikt-palette-lightgrey); font-size: 0.9em; width: 100%; box-sizing: border-box; padding: 3px 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-amber-1); } .wiktku-etym-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-indigo-1); } .wiktku-pron-wrap textarea.wiktku-pron-text { background: var(--wikt-palette-amber-0); } .wiktku-etym-wrap textarea.wiktku-etym-text { background: var(--wikt-palette-indigo-0); } /* ── POS section tabs ── */ #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 10px; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 3px 3px 0 0; background: var(--wikt-palette-dullcyan); cursor: pointer; font-size: 0.9em; user-select: none; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white); border-bottom-color: var(--wikt-palette-white); font-weight: bold; } .wiktku-lang-row-list { margin: 0; padding: 0; list-style: none; } .wiktku-lang-row { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 4px; flex-wrap: wrap; } .wiktku-lang-row-words { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 3px; } /* ── Werger (translation) groups — one per gloss ── */ #wiktku-start-trans .wiktku-trans-group { margin-bottom: 0.8em; } #wiktku-start-trans .wiktku-trans-group:last-child { margin-bottom: 0; } #wiktku-start-trans .wiktku-trans-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } /* ── Bi zaravayên din (dialect) groups — one per gloss, mirrors Werger ── */ #wiktku-start-zarava .wiktku-zarava-group { margin-bottom: 0.8em; } #wiktku-start-zarava .wiktku-zarava-group:last-child { margin-bottom: 0; } #wiktku-start-zarava .wiktku-zarava-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } .wiktku-dfn-jeder-group { margin-top: 4px; } /* Drag handle for reordering citation rows */ .wiktku-jeder-drag-handle { display: inline-block; cursor: grab; color: var(--wikt-palette-lightgrey, #ccc); font-size: 1.2em; line-height: 1; margin-right: 4px; vertical-align: middle; user-select: none; } .wiktku-jeder-drag-handle:active { cursor: grabbing; } .wiktku-dfn-jeder-list .ui-sortable-placeholder { border: 2px dashed var(--wikt-palette-lightgrey, #ccc); background: transparent; visibility: visible !important; height: 2em; } .wiktku-dfn-quote-src-sel { font-size: 0.9em; margin-top: 4px; display: block; } .wiktku-dfn-quote-src-fields { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; } .wiktku-dfn-quote-src-cat-status { display: block; font-size: 0.85em; 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.85em; white-space: nowrap; min-width: 8em; } input.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; } #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-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 4; font-size: 1em; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 6; font-size: 1em; } #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: 3; font-size: 1em; } .wiktku-dfn-nimune-sub { margin-left: 0.5em; padding-left: 0.4em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t { max-width: calc(100vw - 6em); } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { font-size: 1em; min-height: 2.4em; } .wiktku-dfn-quote-src-row label { min-width: 6em; } input.wiktku-dfn-quote-src-input { width: 100%; flex: 1 1 100%; } } @media (prefers-color-scheme: dark) { ul.wiktku-ac-results { background: var(--wikt-palette-white, #1e1e1e); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9, #3a6ea8); color: #fff; } .wiktku-start-step { border-bottom-color: var(--wikt-palette-lightgrey, #444); } .wiktku-addlang-notice { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-preview-panel, #wiktku-live-preview-panel { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-section-tabs .wiktku-section-tab { background: var(--wikt-palette-dullcyan, #1e2d3a); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white, #121212); border-bottom-color: var(--wikt-palette-white, #121212); } } /* ── "Zimanek lê zêde bike" portlet link icon (Minerva/mobile) ── */ /* Minerva auto-generates the icon span class from the portlet link id. */ /* Uses Feather-core-edit.svg from Wikimedia Commons via CSS mask, matching */ /* the exact pattern Minerva uses for all its own icons. */ .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; } ltjooka2zs7obi9g96b72our7b3cq9y 6291595 6291589 2026-07-26T19:31:06Z Wikihez 7344 6291595 css text/css /* =================================================================== MediaWiki:Gadget-start.js — companion styles Copy this file into MediaWiki:Common.css (or a dedicated gadget CSS page) so that the JS gadget no longer needs to inject styles at runtime. Requires MediaWiki:Gadget-Palette.css to be loaded for the --wikt-palette-* custom properties. =================================================================== */ /* ── Continue/submit button── */ .wiktku-btn { padding: 0.4em 1.2em; font-size: 1em; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 2px; background: var(--wikt-palette-white); cursor: pointer; } .wiktku-btn:hover { border-color: var(--wikt-palette-blue-9, #72777d); } .wiktku-btn:disabled { cursor: default; opacity: 0.5; } .wiktku-btn-progressive { background: var(--wikt-palette-blue-9); border-color: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } .wiktku-btn-progressive:hover:not(:disabled) { filter: brightness(1.1); } .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); } .wiktku-start-step { padding: 1em 0; border-bottom: 1px solid #e0e0e0; } .wiktku-start-step-question { font-size: 125%; } .wiktku-start-step label { padding-right: 2em; } input[type="text"].wiktku-start-step-field { width: 100%; } input[type="text"].wiktku-start-step-field.wiktku-start-narrow { width: 20em; } select.wiktku-start-narrow { 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.6em; } #wiktku-start-form textarea.wiktku-start-step-field, #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield { resize: both !important; overflow: auto !important; box-sizing: border-box !important; vertical-align: top; } #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow { width: 20em; min-width: 8em; height: calc(1em + 8px); min-height: calc(1em + 8px); padding-top: 2px !important; padding-bottom: 2px !important; line-height: 1.4 !important; } /* ── Mobile autocomplete widget ── */ .wiktku-native-select-wrap { display: inline-block; position: relative; } .wiktku-native-select-wrap input[type="search"] { display: block; width: 20em; max-width: 100%; box-sizing: border-box; } ul.wiktku-ac-results { position: absolute; z-index: 9999; top: 100%; bottom: auto; left: 0; background: var(--wikt-palette-white); border: 1px solid var(--wikt-palette-lightgrey); margin: 0; padding: 0; list-style: none; width: 20em; max-width: 90vw; max-height: 14em; overflow-y: scroll; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; } ul.wiktku-ac-results li { padding: 6px 10px; cursor: pointer; font-size: 0.95em; min-height: 2.4em; display: flex; align-items: center; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } /* ── Wikitext preview panel ── */ #wiktku-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); white-space: pre-wrap; word-break: break-word; font-family: monospace; font-size: 0.9em; } /* ── Live rendered preview panel (desktop only) ── */ #wiktku-live-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); word-break: break-word; min-height: 2em; } /* ── Preview container (holds wikitext + live panels) ── */ #wiktku-preview-container { display: flex; flex-wrap: wrap; gap: 1em; margin-top: 0.5em; } #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 .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text, #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 2; width: 100%; box-sizing: border-box; height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); min-height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); } #wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 3; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 4; } .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.4 !important; font-size: 0.9em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr { background: var(--wikt-palette-cyan); } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t { background: var(--wikt-palette-palegreen); } /* ── Template selector row inside a sense block ── */ .wiktku-dfn-tpl-row { margin-bottom: 4px; } .wiktku-dfn-tpl-select { font-size: 0.9em; } input.wiktku-dfn-tpl-title { width: 20em; max-width: 100%; box-sizing: border-box; } .wiktku-pron-wrap, .wiktku-etym-wrap { display: flex; flex-direction: column; gap: 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { border: 1px solid var(--wikt-palette-lightgrey); font-size: 0.9em; width: 100%; box-sizing: border-box; padding: 3px 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-amber-1); } .wiktku-etym-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-indigo-1); } .wiktku-pron-wrap textarea.wiktku-pron-text { background: var(--wikt-palette-amber-0); } .wiktku-etym-wrap textarea.wiktku-etym-text { background: var(--wikt-palette-indigo-0); } /* ── POS section tabs ── */ #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 10px; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 3px 3px 0 0; background: var(--wikt-palette-dullcyan); cursor: pointer; font-size: 0.9em; user-select: none; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white); border-bottom-color: var(--wikt-palette-white); font-weight: bold; } .wiktku-lang-row-list { margin: 0; padding: 0; list-style: none; } .wiktku-lang-row { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 4px; flex-wrap: wrap; } .wiktku-lang-row-words { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 3px; } /* ── Werger (translation) groups — one per gloss ── */ #wiktku-start-trans .wiktku-trans-group { margin-bottom: 0.8em; } #wiktku-start-trans .wiktku-trans-group:last-child { margin-bottom: 0; } #wiktku-start-trans .wiktku-trans-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } /* ── Bi zaravayên din (dialect) groups — one per gloss, mirrors Werger ── */ #wiktku-start-zarava .wiktku-zarava-group { margin-bottom: 0.8em; } #wiktku-start-zarava .wiktku-zarava-group:last-child { margin-bottom: 0; } #wiktku-start-zarava .wiktku-zarava-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } .wiktku-dfn-jeder-group { margin-top: 4px; } /* Drag handle for reordering citation rows */ .wiktku-jeder-drag-handle { display: inline-block; cursor: grab; color: var(--wikt-palette-lightgrey, #ccc); font-size: 1.2em; line-height: 1; margin-right: 4px; vertical-align: middle; user-select: none; } .wiktku-jeder-drag-handle:active { cursor: grabbing; } .wiktku-dfn-jeder-list .ui-sortable-placeholder { border: 2px dashed var(--wikt-palette-lightgrey, #ccc); background: transparent; visibility: visible !important; height: 2em; } .wiktku-dfn-quote-src-sel { font-size: 0.9em; margin-top: 4px; display: block; } .wiktku-dfn-quote-src-fields { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; } .wiktku-dfn-quote-citoid { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4em; margin-bottom: 4px; } input.wiktku-dfn-quote-citoid-input { flex: 1 1 24em; width: 24em; max-width: 100%; box-sizing: border-box; } .wiktku-dfn-quote-src-cat-status { display: block; font-size: 0.85em; 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.85em; white-space: nowrap; min-width: 8em; } input.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; } #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-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 4; font-size: 1em; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 6; font-size: 1em; } #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: 3; font-size: 1em; } .wiktku-dfn-nimune-sub { margin-left: 0.5em; padding-left: 0.4em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t { max-width: calc(100vw - 6em); } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { font-size: 1em; min-height: 2.4em; } .wiktku-dfn-quote-src-row label { min-width: 6em; } input.wiktku-dfn-quote-src-input { width: 100%; flex: 1 1 100%; } input.wiktku-dfn-quote-citoid-input { width: 100%; flex: 1 1 100%; } } @media (prefers-color-scheme: dark) { ul.wiktku-ac-results { background: var(--wikt-palette-white, #1e1e1e); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9, #3a6ea8); color: #fff; } .wiktku-start-step { border-bottom-color: var(--wikt-palette-lightgrey, #444); } .wiktku-addlang-notice { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-preview-panel, #wiktku-live-preview-panel { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-section-tabs .wiktku-section-tab { background: var(--wikt-palette-dullcyan, #1e2d3a); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white, #121212); border-bottom-color: var(--wikt-palette-white, #121212); } /* ── Chosen (desktop select-replacement) dropdowns ── */ /* Chosen ships its own light-only stylesheet (white backgrounds, grey text) with no awareness of dark mode, so its closed box and open dropdown panel stayed bright white here. `background:` (shorthand) also clears Chosen's default background-image gradients. */ .chosen-container-single .chosen-single, .chosen-container-multi .chosen-choices { background: var(--wikt-palette-indigo-0, #100e36); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container-active .chosen-single, .chosen-container-active.chosen-with-drop .chosen-single { background: var(--wikt-palette-indigo-1, #1b1751); border-color: var(--wikt-palette-blue-9, #3a6ea8); color: inherit; } .chosen-container .chosen-drop { background: var(--wikt-palette-indigo-0, #100e36); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container .chosen-results { color: inherit; } .chosen-container .chosen-results li.highlighted { background: var(--wikt-palette-blue-9, #3a6ea8); color: #fff; } .chosen-container .chosen-results li.disabled-result, .chosen-container .chosen-results li.result-selected { color: var(--wikt-palette-lightgrey, #767676); } .chosen-container .chosen-results li.no-results { background: var(--wikt-palette-indigo-1, #1b1751); color: inherit; } .chosen-container .chosen-search input[type="text"] { background: var(--wikt-palette-indigo-1, #1b1751); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container-multi .chosen-choices li.search-field input[type="text"] { color: inherit; } .chosen-container-multi .chosen-choices li.search-choice { background: var(--wikt-palette-indigo-1, #1b1751); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } } /* ── "Zimanek lê zêde bike" portlet link icon (Minerva/mobile) ── */ /* Minerva auto-generates the icon span class from the portlet link id. */ /* Uses Feather-core-edit.svg from Wikimedia Commons via CSS mask, matching */ /* the exact pattern Minerva uses for all its own icons. */ .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; } cig5wslfqz9w2mvstxbhqmwz0k6wa8e 6291608 6291595 2026-07-26T20:51:26Z Wikihez 7344 6291608 css text/css /* =================================================================== MediaWiki:Gadget-start.js — companion styles Copy this file into MediaWiki:Common.css (or a dedicated gadget CSS page) so that the JS gadget no longer needs to inject styles at runtime. Requires MediaWiki:Gadget-Palette.css to be loaded for the --wikt-palette-* custom properties. =================================================================== */ /* ── Continue/submit button── */ .wiktku-btn { padding: 0.4em 1.2em; font-size: 1em; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 2px; background: var(--wikt-palette-white); cursor: pointer; } .wiktku-btn:hover { border-color: var(--wikt-palette-blue-9, #72777d); } .wiktku-btn:disabled { cursor: default; opacity: 0.5; } .wiktku-btn-progressive { background: var(--wikt-palette-blue-9); border-color: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } .wiktku-btn-progressive:hover:not(:disabled) { filter: brightness(1.1); } .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); } .wiktku-start-step { padding: 1em 0; border-bottom: 1px solid #e0e0e0; } .wiktku-start-step-question { font-size: 125%; } .wiktku-start-step label { padding-right: 2em; } input[type="text"].wiktku-start-step-field { width: 100%; } input[type="text"].wiktku-start-step-field.wiktku-start-narrow { width: 20em; } select.wiktku-start-narrow { 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.6em; } #wiktku-start-form textarea.wiktku-start-step-field, #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield { resize: both !important; overflow: auto !important; box-sizing: border-box !important; vertical-align: top; } #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow { width: 20em; min-width: 8em; height: calc(1em + 8px); min-height: calc(1em + 8px); padding-top: 2px !important; padding-bottom: 2px !important; line-height: 1.4 !important; } /* ── Mobile autocomplete widget ── */ .wiktku-native-select-wrap { display: inline-block; position: relative; } .wiktku-native-select-wrap input[type="search"] { display: block; width: 20em; max-width: 100%; box-sizing: border-box; } ul.wiktku-ac-results { position: absolute; z-index: 9999; top: 100%; bottom: auto; left: 0; background: var(--wikt-palette-white); border: 1px solid var(--wikt-palette-lightgrey); margin: 0; padding: 0; list-style: none; width: 20em; max-width: 90vw; max-height: 14em; overflow-y: scroll; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; } ul.wiktku-ac-results li { padding: 6px 10px; cursor: pointer; font-size: 0.95em; min-height: 2.4em; display: flex; align-items: center; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } /* ── Wikitext preview panel ── */ #wiktku-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); white-space: pre-wrap; word-break: break-word; font-family: monospace; font-size: 0.9em; } /* ── Live rendered preview panel (desktop only) ── */ #wiktku-live-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); word-break: break-word; min-height: 2em; } /* ── Preview container (holds wikitext + live panels) ── */ #wiktku-preview-container { display: flex; flex-wrap: wrap; gap: 1em; margin-top: 0.5em; } #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 .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text, #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 2; width: 100%; box-sizing: border-box; height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); min-height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); } #wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 3; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 4; } .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.4 !important; font-size: 0.9em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr { background: var(--wikt-palette-cyan); } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t { background: var(--wikt-palette-palegreen); } /* ── Template selector row inside a sense block ── */ .wiktku-dfn-tpl-row { margin-bottom: 4px; } .wiktku-dfn-tpl-select { font-size: 0.9em; } input.wiktku-dfn-tpl-title { width: 20em; max-width: 100%; box-sizing: border-box; } .wiktku-pron-wrap, .wiktku-etym-wrap { display: flex; flex-direction: column; gap: 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { border: 1px solid var(--wikt-palette-lightgrey); font-size: 0.9em; width: 100%; box-sizing: border-box; padding: 3px 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-amber-1); } .wiktku-etym-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-lightindigo-1); } .wiktku-pron-wrap textarea.wiktku-pron-text { background: var(--wikt-palette-amber-0); } .wiktku-etym-wrap textarea.wiktku-etym-text { background: var(--wikt-palette-lightindigo-0); } /* ── POS section tabs ── */ #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 10px; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 3px 3px 0 0; background: var(--wikt-palette-dullcyan); cursor: pointer; font-size: 0.9em; user-select: none; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white); border-bottom-color: var(--wikt-palette-white); font-weight: bold; } .wiktku-lang-row-list { margin: 0; padding: 0; list-style: none; } .wiktku-lang-row { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 4px; flex-wrap: wrap; } .wiktku-lang-row-words { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 3px; } /* ── Werger (translation) groups — one per gloss ── */ #wiktku-start-trans .wiktku-trans-group { margin-bottom: 0.8em; } #wiktku-start-trans .wiktku-trans-group:last-child { margin-bottom: 0; } #wiktku-start-trans .wiktku-trans-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } /* ── Bi zaravayên din (dialect) groups — one per gloss, mirrors Werger ── */ #wiktku-start-zarava .wiktku-zarava-group { margin-bottom: 0.8em; } #wiktku-start-zarava .wiktku-zarava-group:last-child { margin-bottom: 0; } #wiktku-start-zarava .wiktku-zarava-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } .wiktku-dfn-jeder-group { margin-top: 4px; } /* Drag handle for reordering citation rows */ .wiktku-jeder-drag-handle { display: inline-block; cursor: grab; color: var(--wikt-palette-lightgrey, #ccc); font-size: 1.2em; line-height: 1; margin-right: 4px; vertical-align: middle; user-select: none; } .wiktku-jeder-drag-handle:active { cursor: grabbing; } .wiktku-dfn-jeder-list .ui-sortable-placeholder { border: 2px dashed var(--wikt-palette-lightgrey, #ccc); background: transparent; visibility: visible !important; height: 2em; } .wiktku-dfn-quote-src-sel { font-size: 0.9em; margin-top: 4px; display: block; } .wiktku-dfn-quote-src-fields { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; } .wiktku-dfn-quote-citoid { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4em; margin-bottom: 4px; } input.wiktku-dfn-quote-citoid-input { flex: 1 1 24em; width: 24em; max-width: 100%; box-sizing: border-box; } .wiktku-dfn-quote-src-cat-status { display: block; font-size: 0.85em; 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.85em; white-space: nowrap; min-width: 8em; } input.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; } #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-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 4; font-size: 1em; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 6; font-size: 1em; } #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: 3; font-size: 1em; } .wiktku-dfn-nimune-sub { margin-left: 0.5em; padding-left: 0.4em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t { max-width: calc(100vw - 6em); } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { font-size: 1em; min-height: 2.4em; } .wiktku-dfn-quote-src-row label { min-width: 6em; } input.wiktku-dfn-quote-src-input { width: 100%; flex: 1 1 100%; } input.wiktku-dfn-quote-citoid-input { width: 100%; flex: 1 1 100%; } } @media (prefers-color-scheme: dark) { ul.wiktku-ac-results { background: var(--wikt-palette-white, #1e1e1e); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9, #3a6ea8); color: #fff; } .wiktku-start-step { border-bottom-color: var(--wikt-palette-lightgrey, #444); } .wiktku-addlang-notice { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-preview-panel, #wiktku-live-preview-panel { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-section-tabs .wiktku-section-tab { background: var(--wikt-palette-dullcyan, #1e2d3a); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white, #121212); border-bottom-color: var(--wikt-palette-white, #121212); } /* ── Chosen (desktop select-replacement) dropdowns ── */ /* Chosen ships its own light-only stylesheet (white backgrounds, grey text) with no awareness of dark mode, so its closed box and open dropdown panel stayed bright white here. `background:` (shorthand) also clears Chosen's default background-image gradients. */ .chosen-container-single .chosen-single, .chosen-container-multi .chosen-choices { background: var(--wikt-palette-lightindigo-0, #100e36); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container-active .chosen-single, .chosen-container-active.chosen-with-drop .chosen-single { background: var(--wikt-palette-lightindigo-1, #1b1751); border-color: var(--wikt-palette-blue-9, #3a6ea8); color: inherit; } .chosen-container .chosen-drop { background: var(--wikt-palette-lightindigo-0, #100e36); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container .chosen-results { color: inherit; } .chosen-container .chosen-results li.highlighted { background: var(--wikt-palette-blue-9, #3a6ea8); color: #fff; } .chosen-container .chosen-results li.disabled-result, .chosen-container .chosen-results li.result-selected { color: var(--wikt-palette-lightgrey, #767676); } .chosen-container .chosen-results li.no-results { background: var(--wikt-palette-lightindigo-1, #1b1751); color: inherit; } .chosen-container .chosen-search input[type="text"] { background: var(--wikt-palette-lightindigo-1, #1b1751); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container-multi .chosen-choices li.search-field input[type="text"] { color: inherit; } .chosen-container-multi .chosen-choices li.search-choice { background: var(--wikt-palette-lightindigo-1, #1b1751); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } } /* ── "Zimanek lê zêde bike" portlet link icon (Minerva/mobile) ── */ /* Minerva auto-generates the icon span class from the portlet link id. */ /* Uses Feather-core-edit.svg from Wikimedia Commons via CSS mask, matching */ /* the exact pattern Minerva uses for all its own icons. */ .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; } 3kv2yomt27oq5en490gddcf7nbwmhj1 6291609 6291608 2026-07-26T20:53:46Z Wikihez 7344 6291609 css text/css /* =================================================================== MediaWiki:Gadget-start.js — companion styles Copy this file into MediaWiki:Common.css (or a dedicated gadget CSS page) so that the JS gadget no longer needs to inject styles at runtime. Requires MediaWiki:Gadget-Palette.css to be loaded for the --wikt-palette-* custom properties. =================================================================== */ /* ── Continue/submit button── */ .wiktku-btn { padding: 0.4em 1.2em; font-size: 1em; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 2px; background: var(--wikt-palette-white); cursor: pointer; } .wiktku-btn:hover { border-color: var(--wikt-palette-blue-9, #72777d); } .wiktku-btn:disabled { cursor: default; opacity: 0.5; } .wiktku-btn-progressive { background: var(--wikt-palette-blue-9); border-color: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } .wiktku-btn-progressive:hover:not(:disabled) { filter: brightness(1.1); } .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); } .wiktku-start-step { padding: 1em 0; border-bottom: 1px solid #e0e0e0; } .wiktku-start-step-question { font-size: 125%; } .wiktku-start-step label { padding-right: 2em; } input[type="text"].wiktku-start-step-field { width: 100%; } input[type="text"].wiktku-start-step-field.wiktku-start-narrow { width: 20em; } select.wiktku-start-narrow { 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.6em; } #wiktku-start-form textarea.wiktku-start-step-field, #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield { resize: both !important; overflow: auto !important; box-sizing: border-box !important; vertical-align: top; } #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow { width: 20em; min-width: 8em; height: calc(1em + 8px); min-height: calc(1em + 8px); padding-top: 2px !important; padding-bottom: 2px !important; line-height: 1.4 !important; } /* ── Mobile autocomplete widget ── */ .wiktku-native-select-wrap { display: inline-block; position: relative; } .wiktku-native-select-wrap input[type="search"] { display: block; width: 20em; max-width: 100%; box-sizing: border-box; } ul.wiktku-ac-results { position: absolute; z-index: 9999; top: 100%; bottom: auto; left: 0; background: var(--wikt-palette-white); border: 1px solid var(--wikt-palette-lightgrey); margin: 0; padding: 0; list-style: none; width: 20em; max-width: 90vw; max-height: 14em; overflow-y: scroll; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; } ul.wiktku-ac-results li { padding: 6px 10px; cursor: pointer; font-size: 0.95em; min-height: 2.4em; display: flex; align-items: center; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } /* ── Wikitext preview panel ── */ #wiktku-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); white-space: pre-wrap; word-break: break-word; font-family: monospace; font-size: 0.9em; } /* ── Live rendered preview panel (desktop only) ── */ #wiktku-live-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); word-break: break-word; min-height: 2em; } /* ── Preview container (holds wikitext + live panels) ── */ #wiktku-preview-container { display: flex; flex-wrap: wrap; gap: 1em; margin-top: 0.5em; } #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 .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text, #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 2; width: 100%; box-sizing: border-box; height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); min-height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); } #wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 3; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 4; } .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.4 !important; font-size: 0.9em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr { background: var(--wikt-palette-cyan); } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t { background: var(--wikt-palette-palegreen); } /* ── Template selector row inside a sense block ── */ .wiktku-dfn-tpl-row { margin-bottom: 4px; } .wiktku-dfn-tpl-select { font-size: 0.9em; } input.wiktku-dfn-tpl-title { width: 20em; max-width: 100%; box-sizing: border-box; } .wiktku-pron-wrap, .wiktku-etym-wrap { display: flex; flex-direction: column; gap: 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { border: 1px solid var(--wikt-palette-lightgrey); font-size: 0.9em; width: 100%; box-sizing: border-box; padding: 3px 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-amber-1); } .wiktku-etym-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-lightindigo-5); } .wiktku-pron-wrap textarea.wiktku-pron-text { background: var(--wikt-palette-amber-0); } .wiktku-etym-wrap textarea.wiktku-etym-text { background: var(--wikt-palette-lightindigo-5); } /* ── POS section tabs ── */ #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 10px; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 3px 3px 0 0; background: var(--wikt-palette-dullcyan); cursor: pointer; font-size: 0.9em; user-select: none; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white); border-bottom-color: var(--wikt-palette-white); font-weight: bold; } .wiktku-lang-row-list { margin: 0; padding: 0; list-style: none; } .wiktku-lang-row { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 4px; flex-wrap: wrap; } .wiktku-lang-row-words { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 3px; } /* ── Werger (translation) groups — one per gloss ── */ #wiktku-start-trans .wiktku-trans-group { margin-bottom: 0.8em; } #wiktku-start-trans .wiktku-trans-group:last-child { margin-bottom: 0; } #wiktku-start-trans .wiktku-trans-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } /* ── Bi zaravayên din (dialect) groups — one per gloss, mirrors Werger ── */ #wiktku-start-zarava .wiktku-zarava-group { margin-bottom: 0.8em; } #wiktku-start-zarava .wiktku-zarava-group:last-child { margin-bottom: 0; } #wiktku-start-zarava .wiktku-zarava-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } .wiktku-dfn-jeder-group { margin-top: 4px; } /* Drag handle for reordering citation rows */ .wiktku-jeder-drag-handle { display: inline-block; cursor: grab; color: var(--wikt-palette-lightgrey, #ccc); font-size: 1.2em; line-height: 1; margin-right: 4px; vertical-align: middle; user-select: none; } .wiktku-jeder-drag-handle:active { cursor: grabbing; } .wiktku-dfn-jeder-list .ui-sortable-placeholder { border: 2px dashed var(--wikt-palette-lightgrey, #ccc); background: transparent; visibility: visible !important; height: 2em; } .wiktku-dfn-quote-src-sel { font-size: 0.9em; margin-top: 4px; display: block; } .wiktku-dfn-quote-src-fields { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; } .wiktku-dfn-quote-citoid { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4em; margin-bottom: 4px; } input.wiktku-dfn-quote-citoid-input { flex: 1 1 24em; width: 24em; max-width: 100%; box-sizing: border-box; } .wiktku-dfn-quote-src-cat-status { display: block; font-size: 0.85em; 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.85em; white-space: nowrap; min-width: 8em; } input.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; } #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-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 4; font-size: 1em; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 6; font-size: 1em; } #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: 3; font-size: 1em; } .wiktku-dfn-nimune-sub { margin-left: 0.5em; padding-left: 0.4em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t { max-width: calc(100vw - 6em); } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { font-size: 1em; min-height: 2.4em; } .wiktku-dfn-quote-src-row label { min-width: 6em; } input.wiktku-dfn-quote-src-input { width: 100%; flex: 1 1 100%; } input.wiktku-dfn-quote-citoid-input { width: 100%; flex: 1 1 100%; } } @media (prefers-color-scheme: dark) { ul.wiktku-ac-results { background: var(--wikt-palette-white, #1e1e1e); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9, #3a6ea8); color: #fff; } .wiktku-start-step { border-bottom-color: var(--wikt-palette-lightgrey, #444); } .wiktku-addlang-notice { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-preview-panel, #wiktku-live-preview-panel { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-section-tabs .wiktku-section-tab { background: var(--wikt-palette-dullcyan, #1e2d3a); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white, #121212); border-bottom-color: var(--wikt-palette-white, #121212); } /* ── Chosen (desktop select-replacement) dropdowns ── */ /* Chosen ships its own light-only stylesheet (white backgrounds, grey text) with no awareness of dark mode, so its closed box and open dropdown panel stayed bright white here. `background:` (shorthand) also clears Chosen's default background-image gradients. */ .chosen-container-single .chosen-single, .chosen-container-multi .chosen-choices { background: var(--wikt-palette-lightindigo-5, #100e36); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container-active .chosen-single, .chosen-container-active.chosen-with-drop .chosen-single { background: var(--wikt-palette-lightindigo-5, #1b1751); border-color: var(--wikt-palette-blue-9, #3a6ea8); color: inherit; } .chosen-container .chosen-drop { background: var(--wikt-palette-lightindigo-5, #100e36); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container .chosen-results { color: inherit; } .chosen-container .chosen-results li.highlighted { background: var(--wikt-palette-blue-9, #3a6ea8); color: #fff; } .chosen-container .chosen-results li.disabled-result, .chosen-container .chosen-results li.result-selected { color: var(--wikt-palette-lightgrey, #767676); } .chosen-container .chosen-results li.no-results { background: var(--wikt-palette-lightindigo-5, #1b1751); color: inherit; } .chosen-container .chosen-search input[type="text"] { background: var(--wikt-palette-lightindigo-5, #1b1751); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container-multi .chosen-choices li.search-field input[type="text"] { color: inherit; } .chosen-container-multi .chosen-choices li.search-choice { background: var(--wikt-palette-lightindigo-5, #1b1751); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } } /* ── "Zimanek lê zêde bike" portlet link icon (Minerva/mobile) ── */ /* Minerva auto-generates the icon span class from the portlet link id. */ /* Uses Feather-core-edit.svg from Wikimedia Commons via CSS mask, matching */ /* the exact pattern Minerva uses for all its own icons. */ .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; } ahfgooalkfmculw96v8p06m1zux7ek3 6291610 6291609 2026-07-26T21:25:31Z Wikihez 7344 6291610 css text/css /* =================================================================== MediaWiki:Gadget-start.js — companion styles Copy this file into MediaWiki:Common.css (or a dedicated gadget CSS page) so that the JS gadget no longer needs to inject styles at runtime. Requires MediaWiki:Gadget-Palette.css to be loaded for the --wikt-palette-* custom properties. =================================================================== */ /* ── Continue/submit button── */ .wiktku-btn { padding: 0.4em 1.2em; font-size: 1em; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 2px; background: var(--wikt-palette-white); cursor: pointer; } .wiktku-btn:hover { border-color: var(--wikt-palette-blue-9, #72777d); } .wiktku-btn:disabled { cursor: default; opacity: 0.5; } .wiktku-btn-progressive { background: var(--wikt-palette-blue-9); border-color: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } .wiktku-btn-progressive:hover:not(:disabled) { filter: brightness(1.1); } .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); } .wiktku-start-step { padding: 1em 0; border-bottom: 1px solid #e0e0e0; } .wiktku-start-step-question { font-size: 125%; } .wiktku-start-step label { padding-right: 2em; } input[type="text"].wiktku-start-step-field { width: 100%; } input[type="text"].wiktku-start-step-field.wiktku-start-narrow { width: 20em; } select.wiktku-start-narrow { 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.6em; } #wiktku-start-form textarea.wiktku-start-step-field, #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield { resize: both !important; overflow: auto !important; box-sizing: border-box !important; vertical-align: top; } #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow { width: 20em; min-width: 8em; height: calc(1em + 8px); min-height: calc(1em + 8px); padding-top: 2px !important; padding-bottom: 2px !important; line-height: 1.4 !important; } /* ── Mobile autocomplete widget ── */ .wiktku-native-select-wrap { display: inline-block; position: relative; } .wiktku-native-select-wrap input[type="search"] { display: block; width: 20em; max-width: 100%; box-sizing: border-box; } ul.wiktku-ac-results { position: absolute; z-index: 9999; top: 100%; bottom: auto; left: 0; background: var(--wikt-palette-white); border: 1px solid var(--wikt-palette-lightgrey); margin: 0; padding: 0; list-style: none; width: 20em; max-width: 90vw; max-height: 14em; overflow-y: scroll; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; } ul.wiktku-ac-results li { padding: 6px 10px; cursor: pointer; font-size: 0.95em; min-height: 2.4em; display: flex; align-items: center; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } /* ── Wikitext preview panel ── */ #wiktku-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); white-space: pre-wrap; word-break: break-word; font-family: monospace; font-size: 0.9em; } /* ── Live rendered preview panel (desktop only) ── */ #wiktku-live-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); word-break: break-word; min-height: 2em; } /* ── Preview container (holds wikitext + live panels) ── */ #wiktku-preview-container { display: flex; flex-wrap: wrap; gap: 1em; margin-top: 0.5em; } #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 .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text, #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 2; width: 100%; box-sizing: border-box; height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); min-height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); } #wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 3; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 4; } .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.4 !important; font-size: 0.9em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr { background: var(--wikt-palette-cyan); } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t { background: var(--wikt-palette-palegreen); } /* ── Template selector row inside a sense block ── */ .wiktku-dfn-tpl-row { margin-bottom: 4px; } .wiktku-dfn-tpl-select { font-size: 0.9em; } input.wiktku-dfn-tpl-title { width: 20em; max-width: 100%; box-sizing: border-box; } .wiktku-pron-wrap, .wiktku-etym-wrap { display: flex; flex-direction: column; gap: 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { border: 1px solid var(--wikt-palette-lightgrey); font-size: 0.9em; width: 100%; box-sizing: border-box; padding: 3px 4px; } .wiktku-pron-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-amber-1); } .wiktku-etym-wrap select.wiktku-tpl-insert-select { background: var(--wikt-palette-cyan-3); } .wiktku-pron-wrap textarea.wiktku-pron-text { background: var(--wikt-palette-amber-0); } .wiktku-etym-wrap textarea.wiktku-etym-text { background: var(--wikt-palette-cyan-3); } /* ── POS section tabs ── */ #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 10px; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 3px 3px 0 0; background: var(--wikt-palette-dullcyan); cursor: pointer; font-size: 0.9em; user-select: none; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white); border-bottom-color: var(--wikt-palette-white); font-weight: bold; } .wiktku-lang-row-list { margin: 0; padding: 0; list-style: none; } .wiktku-lang-row { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 4px; flex-wrap: wrap; } .wiktku-lang-row-words { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 3px; } /* ── Werger (translation) groups — one per gloss ── */ #wiktku-start-trans .wiktku-trans-group { margin-bottom: 0.8em; } #wiktku-start-trans .wiktku-trans-group:last-child { margin-bottom: 0; } #wiktku-start-trans .wiktku-trans-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } /* ── Bi zaravayên din (dialect) groups — one per gloss, mirrors Werger ── */ #wiktku-start-zarava .wiktku-zarava-group { margin-bottom: 0.8em; } #wiktku-start-zarava .wiktku-zarava-group:last-child { margin-bottom: 0; } #wiktku-start-zarava .wiktku-zarava-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } .wiktku-dfn-jeder-group { margin-top: 4px; } /* Drag handle for reordering citation rows */ .wiktku-jeder-drag-handle { display: inline-block; cursor: grab; color: var(--wikt-palette-lightgrey, #ccc); font-size: 1.2em; line-height: 1; margin-right: 4px; vertical-align: middle; user-select: none; } .wiktku-jeder-drag-handle:active { cursor: grabbing; } .wiktku-dfn-jeder-list .ui-sortable-placeholder { border: 2px dashed var(--wikt-palette-lightgrey, #ccc); background: transparent; visibility: visible !important; height: 2em; } .wiktku-dfn-quote-src-sel { font-size: 0.9em; margin-top: 4px; display: block; } .wiktku-dfn-quote-src-fields { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; } .wiktku-dfn-quote-citoid { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4em; margin-bottom: 4px; } input.wiktku-dfn-quote-citoid-input { flex: 1 1 24em; width: 24em; max-width: 100%; box-sizing: border-box; } .wiktku-dfn-quote-src-cat-status { display: block; font-size: 0.85em; 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.85em; white-space: nowrap; min-width: 8em; } input.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; } #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-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 4; font-size: 1em; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 6; font-size: 1em; } #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: 3; font-size: 1em; } .wiktku-dfn-nimune-sub { margin-left: 0.5em; padding-left: 0.4em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t { max-width: calc(100vw - 6em); } .wiktku-pron-wrap select.wiktku-tpl-insert-select, .wiktku-etym-wrap select.wiktku-tpl-insert-select { font-size: 1em; min-height: 2.4em; } .wiktku-dfn-quote-src-row label { min-width: 6em; } input.wiktku-dfn-quote-src-input { width: 100%; flex: 1 1 100%; } input.wiktku-dfn-quote-citoid-input { width: 100%; flex: 1 1 100%; } } @media (prefers-color-scheme: dark) { ul.wiktku-ac-results { background: var(--wikt-palette-white, #1e1e1e); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } ul.wiktku-ac-results li:active { background: var(--wikt-palette-blue-9, #3a6ea8); color: #fff; } .wiktku-start-step { border-bottom-color: var(--wikt-palette-lightgrey, #444); } .wiktku-addlang-notice { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-preview-panel, #wiktku-live-preview-panel { background: var(--wikt-palette-paleblue, #1a2233); border-color: var(--wikt-palette-lightgrey, #444); } #wiktku-section-tabs .wiktku-section-tab { background: var(--wikt-palette-dullcyan, #1e2d3a); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white, #121212); border-bottom-color: var(--wikt-palette-white, #121212); } /* ── Chosen (desktop select-replacement) dropdowns ── */ /* Chosen ships its own light-only stylesheet (white backgrounds, grey text) with no awareness of dark mode, so its closed box and open dropdown panel stayed bright white here. `background:` (shorthand) also clears Chosen's default background-image gradients. */ .chosen-container-single .chosen-single, .chosen-container-multi .chosen-choices { background: var(--wikt-palette-cyan-3, #100e36); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container-active .chosen-single, .chosen-container-active.chosen-with-drop .chosen-single { background: var(--wikt-palette-cyan-3, #1b1751); border-color: var(--wikt-palette-blue-9, #3a6ea8); color: inherit; } .chosen-container .chosen-drop { background: var(--wikt-palette-cyan-3, #100e36); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container .chosen-results { color: inherit; } .chosen-container .chosen-results li.highlighted { background: var(--wikt-palette-blue-9, #3a6ea8); color: #fff; } .chosen-container .chosen-results li.disabled-result, .chosen-container .chosen-results li.result-selected { color: var(--wikt-palette-lightgrey, #767676); } .chosen-container .chosen-results li.no-results { background: var(--wikt-palette-cyan-3, #1b1751); color: inherit; } .chosen-container .chosen-search input[type="text"] { background: var(--wikt-palette-cyan-3, #1b1751); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } .chosen-container-multi .chosen-choices li.search-field input[type="text"] { color: inherit; } .chosen-container-multi .chosen-choices li.search-choice { background: var(--wikt-palette-cyan-3, #1b1751); border-color: var(--wikt-palette-lightgrey, #444); color: inherit; } } /* ── "Zimanek lê zêde bike" portlet link icon (Minerva/mobile) ── */ /* Minerva auto-generates the icon span class from the portlet link id. */ /* Uses Feather-core-edit.svg from Wikimedia Commons via CSS mask, matching */ /* the exact pattern Minerva uses for all its own icons. */ .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; } 30sek4n4x7jpchydlb1gbokyyztpo26 6291629 6291610 2026-07-27T09:36:42Z Wikihez 7344 6291629 css text/css /* =================================================================== MediaWiki:Gadget-start.js — companion styles Copy this file into MediaWiki:Common.css (or a dedicated gadget CSS page). start.js injects no styles at runtime, so this file is the only source of truth for the gadget's appearance — it must be deployed together with start.js. Requires MediaWiki:Gadget-Palette.css to be loaded for the --wikt-palette-* custom properties. Palette.css already re-defines every var for night mode (html.skin-theme-clientpref-night / -os), so rules built from those vars need no dark-mode counterpart here. =================================================================== */ /* ── Continue/submit button── */ .wiktku-btn { padding: 0.4em 1.2em; font-size: 1em; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 2px; background: var(--wikt-palette-white); cursor: pointer; } .wiktku-btn:hover { border-color: var(--wikt-palette-blue-9, #72777d); } .wiktku-btn:disabled { cursor: default; opacity: 0.5; } .wiktku-btn-progressive { background: var(--wikt-palette-blue-9); border-color: var(--wikt-palette-blue-9); color: var(--wikt-palette-white); } .wiktku-btn-progressive:hover:not(:disabled) { filter: brightness(1.1); } .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); } .wiktku-start-step { padding: 1em 0; border-bottom: 1px solid var(--wikt-palette-grey-3, #e0e0e0); } .wiktku-start-step-question { font-size: 125%; } .wiktku-start-step label { padding-right: 2em; } input[type="text"].wiktku-start-step-field { width: 100%; } input[type="text"].wiktku-start-step-field.wiktku-start-narrow { width: 20em; } /* Only reachable on a select that opted out with .wiktku-plain-select — every other select in the form is hidden behind the searchable picker, which carries this width itself (see input.wiktku-ac-input). */ select.wiktku-start-narrow { 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.6em; } #wiktku-start-form textarea.wiktku-start-step-field, #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield { resize: both !important; overflow: auto !important; box-sizing: border-box !important; vertical-align: top; } #wiktku-start-form textarea.wiktku-start-narrow, #wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow { width: 20em; min-width: 8em; height: calc(1em + 8px); min-height: calc(1em + 8px); padding-top: 2px !important; padding-bottom: 2px !important; line-height: 1.4 !important; } /* =================================================================== Searchable dropdown (wrapNativeSelect in start.js) Used for EVERY dropdown in the form, desktop and mobile alike -- jquery.chosen is no longer loaded, so there is no .chosen-* CSS here. Structure: span.wiktku-native-select-wrap > input.wiktku-ac-input (the search box) > ul.wiktku-ac-results (the result list) > select (hidden, holds the value) =================================================================== */ .wiktku-native-select-wrap { display: inline-block; position: relative; vertical-align: top; } /* The search box stands in for the native select, so it matches the width the 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: 4px 6px; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 2px; background: var(--wikt-palette-white); color: inherit; font-size: inherit; font-family: inherit; } 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; } /* Safari/Chrome draw their own clear button and magnifier on input[type=search]; drop them so the box matches every other field. */ 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 and max-height are set inline by showResults(), which flips the list above the box when there is more room up there. The values here are the defaults it starts from. */ 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: 14em; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; background: var(--wikt-palette-white); border: 1px solid var(--wikt-palette-lightgrey); border-radius: 2px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } ul.wiktku-ac-results li { margin: 0; padding: 5px 8px; cursor: pointer; font-size: 0.95em; display: flex; align-items: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* .wiktku-ac-active is the keyboard cursor (arrow keys); hover mirrors it so mouse and keyboard highlight look the same. :active covers the touch press state on mobile. */ 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, and no hover highlight (it sticks after a tap on touch screens). */ @media (pointer: coarse) { ul.wiktku-ac-results li { padding: 6px 10px; min-height: 2.4em; 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.4em; } } /* The insert-a-template pickers keep their tinted backgrounds now that the search box, not the <select>, is what the user actually 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 { background: var(--wikt-palette-amber-1); font-size: 0.9em; } .wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input { background: var(--wikt-palette-cyan-3); font-size: 0.9em; } .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.9em; } /* Language/dialect rows (Ziman, Dûnde, Werger, Zarava) — the picker has to line up with the word fields sitting next to it. */ .wiktku-lang-row .wiktku-native-select-wrap { flex: 0 0 auto; } /* ── Wikitext preview panel ── */ #wiktku-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); white-space: pre-wrap; word-break: break-word; font-family: monospace; font-size: 0.9em; } /* ── Live rendered preview panel (desktop only) ── */ #wiktku-live-preview-panel { margin-top: 1em; padding: 0.8em; background: var(--wikt-palette-paleblue); border: 1px solid var(--wikt-palette-lightgrey); word-break: break-word; min-height: 2em; } /* ── Preview container (holds wikitext + live panels) ── */ #wiktku-preview-container { display: flex; flex-wrap: wrap; gap: 1em; margin-top: 0.5em; } #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 .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text, #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 2; width: 100%; box-sizing: border-box; height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); min-height: calc(var(--wiktku-ta-rows) * 1.4em + 8px); } #wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 3; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 4; } .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.4 !important; font-size: 0.9em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr { background: var(--wikt-palette-cyan); } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t, .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t { background: var(--wikt-palette-palegreen); } /* ── Template selector row inside a sense block ── */ .wiktku-dfn-tpl-row { margin-bottom: 4px; } input.wiktku-dfn-tpl-title { width: 20em; max-width: 100%; box-sizing: border-box; } .wiktku-pron-wrap, .wiktku-etym-wrap { display: flex; flex-direction: column; gap: 4px; } .wiktku-pron-wrap textarea.wiktku-pron-text { background: var(--wikt-palette-amber-0); } .wiktku-etym-wrap textarea.wiktku-etym-text { background: var(--wikt-palette-cyan-3); } /* ── POS section tabs ── */ #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 10px; border: 1px solid var(--wikt-palette-lightgrey); border-radius: 3px 3px 0 0; background: var(--wikt-palette-dullcyan); cursor: pointer; font-size: 0.9em; user-select: none; } #wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active { background: var(--wikt-palette-white); border-bottom-color: var(--wikt-palette-white); font-weight: bold; } .wiktku-lang-row-list { margin: 0; padding: 0; list-style: none; } .wiktku-lang-row { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 4px; flex-wrap: wrap; } .wiktku-lang-row-words { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 3px; } /* ── Werger (translation) groups — one per gloss ── */ #wiktku-start-trans .wiktku-trans-group { margin-bottom: 0.8em; } #wiktku-start-trans .wiktku-trans-group:last-child { margin-bottom: 0; } #wiktku-start-trans .wiktku-trans-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } /* ── Bi zaravayên din (dialect) groups — one per gloss, mirrors Werger ── */ #wiktku-start-zarava .wiktku-zarava-group { margin-bottom: 0.8em; } #wiktku-start-zarava .wiktku-zarava-group:last-child { margin-bottom: 0; } #wiktku-start-zarava .wiktku-zarava-group-label { font-weight: bold; font-size: 0.9em; margin-bottom: 3px; } .wiktku-dfn-jeder-group { margin-top: 4px; } /* Drag handle for reordering citation rows */ .wiktku-jeder-drag-handle { display: inline-block; cursor: grab; color: var(--wikt-palette-lightgrey, #ccc); font-size: 1.2em; line-height: 1; margin-right: 4px; vertical-align: middle; user-select: none; } .wiktku-jeder-drag-handle:active { cursor: grabbing; } .wiktku-dfn-jeder-list .ui-sortable-placeholder { border: 2px dashed var(--wikt-palette-lightgrey, #ccc); background: transparent; visibility: visible !important; height: 2em; } .wiktku-dfn-quote-src-sel { font-size: 0.9em; margin-top: 4px; display: block; } .wiktku-dfn-quote-src-fields { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; } .wiktku-dfn-quote-citoid { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4em; margin-bottom: 4px; } input.wiktku-dfn-quote-citoid-input { flex: 1 1 24em; width: 24em; max-width: 100%; box-sizing: border-box; } .wiktku-dfn-quote-src-cat-status { display: block; font-size: 0.85em; 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.85em; white-space: nowrap; min-width: 8em; } input.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; } /* One column, so let the search box and its list use the full width. */ .wiktku-native-select-wrap, input.wiktku-ac-input, ul.wiktku-ac-results { width: 100%; max-width: 100%; } #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-pron-wrap textarea.wiktku-pron-text, #wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text { --wiktku-ta-rows: 4; font-size: 1em; } #wiktku-start-form textarea.wiktku-cavkani-text { --wiktku-ta-rows: 6; font-size: 1em; } #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: 3; font-size: 1em; } .wiktku-dfn-nimune-sub { margin-left: 0.5em; padding-left: 0.4em; } .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr, .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t { max-width: calc(100vw - 6em); } .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.4em; } .wiktku-dfn-quote-src-row label { min-width: 6em; } input.wiktku-dfn-quote-src-input { width: 100%; flex: 1 1 100%; } input.wiktku-dfn-quote-citoid-input { width: 100%; flex: 1 1 100%; } } /* =================================================================== Night mode MediaWiki:Gadget-Palette.css already re-defines every --wikt-palette-* var for night mode, on html.skin-theme-clientpref-night and (for the "follow OS" setting) html.skin-theme-clientpref-os under a dark OS preference. So any rule above that is built from those vars flips by itself and needs nothing here. What used to live here was a bare @media (prefers-color-scheme: dark) block. That was the wrong hook twice over: it fired even when the reader had explicitly chosen light mode on-wiki, and almost every rule in it just restated the light rule with the same var. The only genuine exceptions were the .chosen-* overrides, and jquery.chosen is no longer loaded at all. Keep this hook for anything that has to be a real colour rather than a palette var — here, only the panel's edge, because a drop shadow does nothing against an equally dark page. The two selectors below mirror Palette.css exactly. Note what is deliberately NOT here: li:active sets `color: var(--wikt-palette-white)`, and that var flips to #101418 at night, so the pressed row is dark-on-light in night mode all by itself (9.3:1). Re-stating that colour here as --wikt-palette-black would make it light-on-light and drop it to 1.7:1. =================================================================== */ @media screen { html.skin-theme-clientpref-night .wiktku-ac-results { /* Lift the panel off an equally dark page: the light-mode drop shadow is invisible on a dark background, so lean on the border instead. */ border-color: var(--wikt-palette-grey-5); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.6); } } @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 2px 8px rgba(0, 0, 0, 0.6); } } /* ── "Zimanek lê zêde bike" portlet link icon (Minerva/mobile) ── */ /* Minerva auto-generates the icon span class from the portlet link id. */ /* Uses Feather-core-edit.svg from Wikimedia Commons via CSS mask, matching */ /* the exact pattern Minerva uses for all its own icons. */ .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; } 47oav1a0k6ppvcz3kvywsui1sxqeqoa Bikarhêner:Wikihez/skrîpt/js/lastedited.js 2 1358139 6291612 6276828 2026-07-26T22:40:01Z Wikihez 7344 6291612 javascript text/javascript /** * Script for ku.wiktionary.org * Automatically shows last edit time for members of "Kategorî:Madeyên ku bên jêbirin" * Highlights items older than 7 days. * Dark-mode aware: responds to prefers-color-scheme and MediaWiki's * Vector 2022 "night" skin theme class. */ (function($, mw) { // Only run on the specific deletion category page if (mw.config.get('wgPageName') !== 'Kategorî:Madeyên_ku_bên_jêbirin') { return; } function injectStyles() { if (document.getElementById('last-edit-tag-styles')) return; var css = '' + '.last-edit-tag {' + ' font-size: 0.82em;' + ' margin-left: 8px;' + ' padding: 1px 4px;' + ' border-radius: 3px;' + ' display: inline-block;' + '}' + '.last-edit-tag--recent {' + ' color: #555;' + ' background-color: #f1f1f1;' + ' border: 1px solid #ddd;' + '}' + '.last-edit-tag--old {' + ' background-color: #d33;' + ' color: #fff;' + ' font-weight: bold;' + '}' + '.last-edit-item--old {' + ' background-color: #fff0f0;' + ' border-left: 4px solid #d33;' + ' padding-left: 6px;' + ' margin-bottom: 1px;' + '}' // System dark mode (browser-level) + '@media (prefers-color-scheme: dark) {' + ' .last-edit-tag--recent {' + ' color: #bbb;' + ' background-color: #2a2a2a;' + ' border: 1px solid #444;' + ' }' + ' .last-edit-tag--old {' + ' background-color: #ff6b6b;' + ' color: #1a1a1a;' + ' }' + ' .last-edit-item--old {' + ' background-color: #3a1f1f;' + ' border-left: 4px solid #ff6b6b;' + ' }' + '}' // MediaWiki Vector 2022 "night" theme (explicit user preference, // takes precedence over the OS-level media query above) + '.skin-theme-clientpref-night .last-edit-tag--recent {' + ' color: #bbb;' + ' background-color: #2a2a2a;' + ' border: 1px solid #444;' + '}' + '.skin-theme-clientpref-night .last-edit-tag--old {' + ' background-color: #ff6b6b;' + ' color: #1a1a1a;' + '}' + '.skin-theme-clientpref-night .last-edit-item--old {' + ' background-color: #3a1f1f;' + ' border-left: 4px solid #ff6b6b;' + '}' // Explicit "day" preference should always win over OS dark mode + '.skin-theme-clientpref-day .last-edit-tag--recent {' + ' color: #555;' + ' background-color: #f1f1f1;' + ' border: 1px solid #ddd;' + '}' + '.skin-theme-clientpref-day .last-edit-tag--old {' + ' background-color: #d33;' + ' color: #fff;' + '}' + '.skin-theme-clientpref-day .last-edit-item--old {' + ' background-color: #fff0f0;' + ' border-left: 4px solid #d33;' + '}'; var $style = $('<style>') .attr('id', 'last-edit-tag-styles') .text(css); $('head').append($style); } function getRelativeKurdishTime(timestamp) { var now = new Date(); var past = new Date(timestamp); var diffInSeconds = Math.floor((now - past) / 1000); // 7 days = 604800 seconds var isOld = diffInSeconds > 604800; var text = "niha"; var intervals = [ { label: 'salan', seconds: 31536000 }, { label: 'mehan', seconds: 2592000 }, { label: 'rojan', seconds: 86400 }, { label: 'saetan', seconds: 3600 }, { label: 'deqîqeyan', seconds: 60 } ]; for (var i = 0; i < intervals.length; i++) { var interval = intervals[i]; var count = Math.floor(diffInSeconds / interval.seconds); if (count >= 1) { text = "berî " + count + " " + interval.label; break; } } return { text: text, isOld: isOld }; } function autoShowLastEdits() { var $links = $('.mw-category-generated a').not('.external'); var titles = []; var linkMap = {}; // Find all links in the category list $links.each(function() { var title = $(this).attr('title'); if (title) { titles.push(title); if (!linkMap[title]) linkMap[title] = []; linkMap[title].push($(this)); } }); if (titles.length === 0) return; var api = new mw.Api(); // Batch requests to API (50 titles at a time) for (var i = 0; i < titles.length; i += 50) { var batch = titles.slice(i, i + 50); api.get({ action: 'query', prop: 'revisions', titles: batch, rvprop: 'timestamp', formatversion: 2 }).done(function(data) { if (!data.query || !data.query.pages) return; data.query.pages.forEach(function(page) { if (page.revisions && page.revisions[0]) { var timeData = getRelativeKurdishTime(page.revisions[0].timestamp); if (linkMap[page.title]) { linkMap[page.title].forEach(function($el) { // Prevent duplicate tags if script runs twice if ($el.parent().find('.last-edit-tag').length) return; var $tag = $('<span>') .addClass('last-edit-tag') .addClass(timeData.isOld ? 'last-edit-tag--old' : 'last-edit-tag--recent') .text('(' + timeData.text + ')'); if (timeData.isOld) { $el.closest('li').addClass('last-edit-item--old'); } $el.after($tag); }); } } }); }); } } // Run automatically when the page is ready $(document).ready(function() { injectStyles(); autoShowLastEdits(); }); })(jQuery, mediaWiki); sgfdr6593yiw5m8w67pe5y4de0ein69 Wîkîferheng:Nû 4 1358184 6291600 2026-07-26T20:09:34Z Wikihez 7344 Ji bo [[MediaWiki:Gadget-start.js]] hat beralîkirin 6291600 wikitext text/x-wiki #BERALÎKIRIN [[MediaWiki:Gadget-start.js]] fbmznk14jksvcz4gz220otwksjm7c6s Wîkîferheng:Palette 4 1358185 6291601 2026-07-26T20:14:22Z Wikihez 7344 [[:en:Wiktionary:Palette]] hat kopîkirin ([[Bikarhêner:Wikihez/skrîpt/js/kopi-enWikt.js|kopi-enWikt.js]]) 6291601 wikitext text/x-wiki {{shortcut|WT:P}} Wiktionary defines a CSS palette of colors that can be used by templates. Using the palette variables instead of hardcoded colors provides automatic support for dark mode, and provides a consistent set of colors for use by templates to maintain a more consistent look-and-feel. The code may be found at [[MediaWiki:Gadget-Palette.css]]. ==Colors== There are two main groups of colors. Those colors without numbers in their names are used by core templates (i.e. templates that are used widely across pages and are not designed for specific languages or groups of languages) and as text colors. Those colors with numbers in their names are the numbered colors, intended for use as background or border colors, e.g. in inflection tables. They are also used by {{şx|inflection-table-top}}. The numbered colors consist of 16 base colors with 10 contrast levels each. These colors were derived algorithmically: see [[Wiktionary:Palette/numbered]] for details. The numbered base colors are red, scarlet, orange, amber, yellow, lime, green, teal, cyan, blue, indigo, purple, magenta, rose, brown and grey, and the indices go from 0 to 9. For example, a blue color that is the closest to the background color has the name <code>blue-0</code>, resulting in the full name <code>--wikt-palette-blue-0</code>. Desaturated shades of all base colors except grey can be obtained by prefixing <code>grey-</code>, e.g. <code>grey-blue-0</code>. ==Usage== Palette colors can be used via CSS using <code>var(...)</code>. '''Bad:''' <syntaxhighlight lang="html"> <span style="color: #660000">some red text</span> </syntaxhighlight> <span style="color: #660000">some red text</span> '''Better:''' <syntaxhighlight lang="html"> <span style="color: var(--wikt-palette-darkred)">some red text</span> </syntaxhighlight> <span style="color: var(--wikt-palette-darkred)">some red text</span> The first snippet will be difficult to read in dark mode, whereas the bottom snippet will automatically invert its colors. It is possible to provide a fallback color which would theoretically activate if for whatever reason the palette variables are absent. This looks something like <code>var(--wikt-palette-blue-0, #f8fafe)</code>. When converting older templates, the pre-existing color is sometimes left as the fallback, but this is not required. ===Recommendations=== When using numbered colors, the colors with index 0, or simply the default page background color, should be used as the background color for main table cells to ensure maximum contrast. Headers should use colors with the index 1, 2, 3 or 4 as the background color depending on their level and required levels of contrast. Colors with indices 7, 8 and 9 should ideally not be used as background colors for elements with text, unless the text color is inverted from the default color. They are mainly useful as text or border colors. ===Shorthand=== With numbered colors, there also exists a shorthand. You can use it by specifying one of the base colors using the <code>data-wikt-palette-p</code> attribute, e.g. <code>data-wikt-palette-p="red"</code>. Then, any elements within such a tag can use <code>--wikt-p-{0-9}</code> to use the palette color <code>--wikt-palette-red-{0-9}</code>, as well as <code>--wikt-p-g-{0-9}</code> to use the palette color <code>--wikt-palette-grey-red-{0-9}</code>. With the base color <code>grey</code>, both give the same color. ==Table== {{box-top|Table of palette variables (long)}} {{#invoke:palette|show}} {{box-bottom}} [[Category:Wiktionary]] hy8qlhhb49akx4rdscqcdo757xjpx2t 6291607 6291601 2026-07-26T20:21:29Z Wikihez 7344 /* */ bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re 6291607 wikitext text/x-wiki {{shortcut|WT:P}} Wiktionary defines a CSS palette of colors that can be used by templates. Using the palette variables instead of hardcoded colors provides automatic support for dark mode, and provides a consistent set of colors for use by templates to maintain a more consistent look-and-feel. The code may be found at [[MediaWiki:Gadget-Palette.css]]. ==Colors== There are two main groups of colors. Those colors without numbers in their names are used by core templates (i.e. templates that are used widely across pages and are not designed for specific languages or groups of languages) and as text colors. Those colors with numbers in their names are the numbered colors, intended for use as background or border colors, e.g. in inflection tables. They are also used by {{şx|inflection-table-top}}. The numbered colors consist of 16 base colors with 10 contrast levels each. These colors were derived algorithmically: see [[Wiktionary:Palette/numbered]] for details. The numbered base colors are red, scarlet, orange, amber, yellow, lime, green, teal, cyan, blue, indigo, purple, magenta, rose, brown and grey, and the indices go from 0 to 9. For example, a blue color that is the closest to the background color has the name <code>blue-0</code>, resulting in the full name <code>--wikt-palette-blue-0</code>. Desaturated shades of all base colors except grey can be obtained by prefixing <code>grey-</code>, e.g. <code>grey-blue-0</code>. ==Usage== Palette colors can be used via CSS using <code>var(...)</code>. '''Bad:''' <syntaxhighlight lang="html"> <span style="color: #660000">some red text</span> </syntaxhighlight> <span style="color: #660000">some red text</span> '''Better:''' <syntaxhighlight lang="html"> <span style="color: var(--wikt-palette-darkred)">some red text</span> </syntaxhighlight> <span style="color: var(--wikt-palette-darkred)">some red text</span> The first snippet will be difficult to read in dark mode, whereas the bottom snippet will automatically invert its colors. It is possible to provide a fallback color which would theoretically activate if for whatever reason the palette variables are absent. This looks something like <code>var(--wikt-palette-blue-0, #f8fafe)</code>. When converting older templates, the pre-existing color is sometimes left as the fallback, but this is not required. ===Recommendations=== When using numbered colors, the colors with index 0, or simply the default page background color, should be used as the background color for main table cells to ensure maximum contrast. Headers should use colors with the index 1, 2, 3 or 4 as the background color depending on their level and required levels of contrast. Colors with indices 7, 8 and 9 should ideally not be used as background colors for elements with text, unless the text color is inverted from the default color. They are mainly useful as text or border colors. ===Shorthand=== With numbered colors, there also exists a shorthand. You can use it by specifying one of the base colors using the <code>data-wikt-palette-p</code> attribute, e.g. <code>data-wikt-palette-p="red"</code>. Then, any elements within such a tag can use <code>--wikt-p-{0-9}</code> to use the palette color <code>--wikt-palette-red-{0-9}</code>, as well as <code>--wikt-p-g-{0-9}</code> to use the palette color <code>--wikt-palette-grey-red-{0-9}</code>. With the base color <code>grey</code>, both give the same color. ==Table== {{veşêre-ser|Table of palette variables (long)}} {{#invoke:palette|show}} {{veşêre-bin}} [[Category:Wîkîferheng]] 5o1zxyyi0si2miv8f2xiwmlvv38guka Wîkîferheng:Palette/numbered 4 1358186 6291602 2026-07-26T20:15:10Z Wikihez 7344 [[:en:Wiktionary:Palette/numbered]] hat kopîkirin ([[Bikarhêner:Wikihez/skrîpt/js/kopi-enWikt.js|kopi-enWikt.js]]) 6291602 wikitext text/x-wiki The process for generating the numbered colors in [[Wiktionary:Palette]] is outlined on this page. ==Algorithm== The basic process is outlined as follows: # Start from a set of base colors # Adjust each color to meet contrast requirements This results in a color set of 10*N colors for N base colors, with each variant of a base color identified by an index 0 through 9. 0 is the closest to the background color and 9 the furthest from the background color. ===Base colors=== {| class="wikitable" |+Overview of basic color names and hex codes !scope="col"| Color name !scope="col"| Hex value !scope="col"| Example |- !scope="row"| red | <code>#e63949</code> || style="background-color:#e63949; min-width: 5em;" | |- !scope="row"| scarlet | <code>#e0562d</code> || style="background-color:#e0562d; min-width: 5em;" | |- !scope="row"| orange | <code>#d9760f</code> || style="background-color:#d9760f; min-width: 5em;" | |- !scope="row"| amber | <code>#cf9211</code> || style="background-color:#cf9211; min-width: 5em;" | |- !scope="row"| yellow | <code>#baa72f</code> || style="background-color:#baa72f; min-width: 5em;" | |- !scope="row"| lime | <code>#97b53c</code> || style="background-color:#97b53c; min-width: 5em;" | |- !scope="row"| green | <code>#60b334</code> || style="background-color:#60b334; min-width: 5em;" | |- !scope="row"| teal | <code>#32b378</code> || style="background-color:#32b378; min-width: 5em;" | |- !scope="row"| cyan | <code>#28a0b5</code> || style="background-color:#28a0b5; min-width: 5em;" | |- !scope="row"| blue | <code>#3271f0</code> || style="background-color:#3271f0; min-width: 5em;" | |- !scope="row"| indigo | <code>#584ee6</code> || style="background-color:#584ee6; min-width: 5em;" | |- !scope="row"| purple | <code>#7f38e8</code> || style="background-color:#7f38e8; min-width: 5em;" | |- !scope="row"| magenta | <code>#9d2cdb</code> || style="background-color:#9d2cdb; min-width: 5em;" | |- !scope="row"| rose | <code>#a6448c</code> || style="background-color:#a6448c; min-width: 5em;" | |- !scope="row"| brown | <code>#856142</code> || style="background-color:#856142; min-width: 5em;" | |- !scope="row"| grey | <code>#666666</code> || style="background-color:#666666; min-width: 5em;" | |} ===Grey colors=== For each base color except grey, a new grey- variant (e.g. red &rarr; grey-red) is created by desaturating the base color by 50%. ===Variants=== Variants are derived from the base color by using a value called an ''offset'' <math>\in [-1, 1]</math> If not otherwise specified, colors are expressed as <math>r, g, b</math> where each value <math>\in [0, 1]</math>. To generate a variant, we use an operation <math>a</math> (<code>adjust</code>) below, which takes the base color <math>r, g, b</math> and the offset <math>o</math>: <math display="block">a(r, g, b, o) = \begin{cases} a_+(r, g, b, o), & \text{if } o > 0 \\ a_-(r, g, b, -o), & \text{if } o < 0 \\ {[r, g, b]}, & \text{if } o = 0 . \end{cases}</math> The operation <math>a_+</math> (<code>lighten</code>) lightens a color by a certain factor <math>p \in [0, 1]</math>: <math display="block">a_+(r, g, b, p) = [(1-p)r+p,(1-p)g+p,(1-p)b+p]</math> The operation <math>a_-</math> (<code>deepen</code>) ''deepens'' a color by a certain factor <math>p \in [0, 1]</math>. It is implemented by converting the color from RGB to HSV (with <math>s, v \in [0, 1]</math>; <math>h</math> is not altered and thus its range is insignificant), applying the operation <math>a_{-hsv}</math> to the HSV values and the factor <math>p</math>, and then converting back to RGB, where: <math display="block">a_{-hsv}(h, s, v, p) = [h, \sqrt{s} (1 - (1 - s)(1 - p)), (1 - p)v]</math> ===Contrast requirements=== The contrast requirements apply with the variant used as the background color and the default text color, either black or white, as the foreground color. In light mode, the text color is the default black text color, <code>#202122</code>. In dark mode, it is the default white text color, <code>#eaecf0</code>. Both of these requirements apply, and whichever is stricter will take priority. In light mode, the contrast is against the black foreground color, and in night mode, against the white foreground color: {| class="wikitable" ! Index !! WCAG !! APCA !! Notes |- | 0 || 15.00 || +100 (light), −94.5 dark) || |- | 1 || 13.25 || +95 (light), −93 (dark) || |- | 2 || 11.50 || ±90 || APCA Bronze Simple: body text preferred contrast is ≥+90 or ≤−90 |- | 3 || 9.25 || ±85 || |- | 4 || 7.50 || ±75 || WCAG: AAA for body text. APCA Bronze Simple: body text minimum contrast is ≥+75 or ≤−75 |- | 5 || 6.00 || ±67.5 || |- | 6 || 4.50 || ±60 || WCAG: AA for body text. APCA Bronze Simple: minimum requirement for content text is ≥+60 or ≤−60 |- | 7 || 3.00 || ±52.5 || |- | 8 || 2.10 || ±45 || |- | 9 || 1.40 || ±30 || APCA Bronze Simple: absolute minimum requirement for any kind of text is ≥+30 or ≤−30 |} Note that the requirements regarding APCA contrast for indices 0 and 1 in dark mode are different from those in light mode, since the contrast of the default foreground against the default background in the default dark color scheme is only approximately −94.7. The color variants, and therefore offsets, that are tried are all those that result in distinct colors when expressed as 6-digit hexadecimal colors. Then, in light mode, we pick the minimum offset for which the quantized color (as if converted to 6-digit hex and back) meets both contrast requirements, and in dark mode, we pick the maximum offset for which the quantized color meets both contrast requirements. This ensures that the color is dark as possible in light mode, and as light as possible in dark mode. ==Implementation== The below is a Python reference implementation of this algorithm. <syntaxhighlight lang="python"> import colorsys import re IDENTIFICATION = "[[Wiktionary:Palette/numbered]]" HEX_COLOR_RGB = re.compile(r"^#[0-9A-Fa-f]{6}$") def _hex_to_rgb_comp(x): return int(x, 16) / 255.0 def hex_to_rgb(hex_color): """ Converts a color in hex format into the internal format. (R, G, B, with each value lying within [0, 1]). """ if not HEX_COLOR_RGB.match(hex_color): raise ValueError("hex color format not supported") r, g, b = hex_color[1:3], hex_color[3:5], hex_color[5:7] return (_hex_to_rgb_comp(r), _hex_to_rgb_comp(g), _hex_to_rgb_comp(b)) def rgb_to_hex(c): """Converts a color into hex format.""" r, g, b = c r = int(round(r * 255.0)) g = int(round(g * 255.0)) b = int(round(b * 255.0)) return "#{:02x}{:02x}{:02x}".format(r, g, b) def quantize(color): """Quantizes a color so that it corresponds exactly to a 6-digit hex color.""" r, g, b = color r = int(round(r * 255)) / 255 g = int(round(g * 255)) / 255 b = int(round(b * 255)) / 255 return r, g, b def _wcag_to_srgb(x): if x <= 0.03928: return x / 12.2 else: return ((x + 0.055) / 1.055) ** 2.4 def wcag_luminance(color): """Computes the sRGB luminance of a color according to WCAG.""" rs, gs, bs = color r = _wcag_to_srgb(rs) g = _wcag_to_srgb(gs) b = _wcag_to_srgb(bs) return 0.2126 * r + 0.7152 * g + 0.0722 * b def wcag_contrast(fgcolor, bgcolor): """Computes the WCAG contrast ratio between two colors.""" fgluma = wcag_luminance(fgcolor) bgluma = wcag_luminance(bgcolor) ratio = (fgluma + 0.05) / (bgluma + 0.05) if ratio < 1: ratio = 1.0 / ratio return ratio def _simple_srgb_luma(color): # <https://github.com/Myndex/SAPC-APCA/blob/master/documentation/APCA-W3-LaTeX.md> # <https://en.wiktionary.org/w/index.php?title=Module:palette&oldid=82494940#L-57--L-94> r, g, b = color s_rco, s_gco, s_bco = 0.2126729, 0.7151522, 0.0721750 main_trc = 2.4 return clamp(0, s_rco * (r ** main_trc) + s_gco * (g ** main_trc) + s_bco * (b ** main_trc), 1) def _apca_f_sc(y_c): # <https://github.com/Myndex/SAPC-APCA/blob/master/documentation/APCA-W3-LaTeX.md> # <https://en.wiktionary.org/w/index.php?title=Module:palette&oldid=82494940#L-57--L-94> if y_c <= 0: return 0 elif y_c <= 0.022: return y_c + (0.022 - y_c) ** 1.414 else: return y_c def apca_contrast(fgcolor, bgcolor): """Computes the APCA contrast value between a foreground and background color.""" # <https://github.com/Myndex/SAPC-APCA/blob/master/documentation/APCA-W3-LaTeX.md> # <https://en.wiktionary.org/w/index.php?title=Module:palette&oldid=82494940#L-57--L-94> ys_fg = _simple_srgb_luma(fgcolor) ys_bg = _simple_srgb_luma(bgcolor) y_fg = _apca_f_sc(ys_fg) y_bg = _apca_f_sc(ys_bg) if y_bg > y_fg: s_apc = (y_bg ** 0.56 - y_fg ** 0.57) * 1.14 else: s_apc = (y_bg ** 0.65 - y_fg ** 0.62) * 1.14 if abs(s_apc) < 0.1: l_c = 0 elif s_apc > 0: l_c = (s_apc - 0.027) * 100 else: l_c = (s_apc + 0.027) * 100 return l_c def clamp(minimum, x, maximum): """Forces the value x between the endpoints minimum and maximum.""" assert minimum <= maximum return max(minimum, min(maximum, x)) def _lighten_comp(component, power): if not 0 <= power <= 1: raise ValueError("power must be within [0, 1]") return power + component * (1 - power) def lighten(color, power): """Lightens a color by a certain power [0, 1].""" if not 0 <= power <= 1: raise ValueError("power must be within [0, 1]") r, g, b = color return (_lighten_comp(r, power), _lighten_comp(g, power), _lighten_comp(b, power)) def deepen(color, power): """Deepens a color by a certain power [0, 1].""" if not 0 <= power <= 1: raise ValueError("power must be within [0, 1]") r, g, b = color h, s, v = colorsys.rgb_to_hsv(r, g, b) s_new = (1 - (1 - s) * (1 - power)) * (s ** 0.5) v_new = v * (1 - power) return colorsys.hsv_to_rgb(h, s_new, v_new) def adjust(color, offset): """ Adjusts a color according to the offset. If the offset is positive, the color is lightened. If the offset is negative, the color is deepened. """ if offset > 0: return lighten(color, offset) elif offset < 0: return deepen(color, -offset) return color def saturate(color, multiplier): r, g, b = color h, s, v = colorsys.rgb_to_hsv(r, g, b) s_new = min(1, s * multiplier) return colorsys.hsv_to_rgb(h, s_new, v) class ContrastedColorMaker: """ Generates variants of the base color that meets contrast requirements as a background color against the specified reference foreground color. """ def __init__(self, base_color, reference, night_mode): self._night_mode = night_mode # generate all possible colors div = 1024 options = {} self._base_color = base_color self._colors = {} self._wcag = [] self._apca = [] for i in range(-div, div + 1): offset = clamp(-1, i / div, 1) color = quantize(adjust(base_color, offset)) if color not in options: options[color] = offset self._colors[offset] = color self._wcag.append((wcag_contrast(reference, color), offset)) self._apca.append((abs(apca_contrast(reference, color)), offset)) self._wcag.sort() self._apca.sort() def for_contrast(self, contrast_wcag, contrast_apca): """ Returns a variant of the color that meets the WCAG and APCA requirements (the latter is optional). If no such color exists, returns None. """ wcag_meets = set(offset for contrast, offset in self._wcag if contrast >= contrast_wcag) if contrast_apca is None: meets = wcag_meets else: apca_meets = set(offset for contrast, offset in self._apca if contrast >= contrast_apca) meets = wcag_meets & apca_meets if meets: return self._colors[(max if self._night_mode else min)(meets)] return None BASE = { "red": hex_to_rgb("#e63949"), "scarlet": hex_to_rgb("#e0562d"), "orange": hex_to_rgb("#d9760f"), "amber": hex_to_rgb("#cf9211"), "yellow": hex_to_rgb("#baa72f"), "lime": hex_to_rgb("#97b53c"), "green": hex_to_rgb("#60b334"), "teal": hex_to_rgb("#32b378"), "cyan": hex_to_rgb("#28a0b5"), "blue": hex_to_rgb("#3271f0"), "indigo": hex_to_rgb("#584ee6"), "purple": hex_to_rgb("#7f38e8"), "magenta": hex_to_rgb("#9d2cdb"), "rose": hex_to_rgb("#a6448c"), "brown": hex_to_rgb("#856142"), } WHITE_BG = "#ffffff" BLACK_FG = "#202122" WHITE_FG = "#eaecf0" BLACK_BG = "#101418" white = hex_to_rgb(WHITE_FG) black = hex_to_rgb(BLACK_FG) CONTRASTS = { 0: (15.00, 100, 94.5), 1: (13.25, 95, 93), 2: (11.50, 90, 90), 3: ( 9.25, 85, 85), 4: ( 7.50, 75, 75), 5: ( 6.00, 67.5, 67.5), 6: ( 4.50, 60, 60), 7: ( 3.00, 52.5, 52.5), 8: ( 2.10, 45, 45), 9: ( 1.40, 30, 30), } # generate -grey- variants. for key, value in list(BASE.items()): BASE["grey-" + key] = saturate(value, 0.5) BASE["grey"] = hex_to_rgb("#666666") colors_light = {} colors_night = {} for base_color_name, base_color in BASE.items(): tints_light = colors_light[base_color_name] = {} tints_night = colors_night[base_color_name] = {} contrasted_color_maker_light = ContrastedColorMaker(base_color, black, False) contrasted_color_maker_night = ContrastedColorMaker(base_color, white, True) for tint_number, (contrast_wcag, contrast_apca, contrast_apca_night) in CONTRASTS.items(): tint_light = contrasted_color_maker_light.for_contrast(contrast_wcag, contrast_apca) if tint_light is None: raise ValueError(f"light mode contrast requirement cannot be met (WCAG: {contrast_wcag}, APCA: {contrast_apca}, base color: {rgb_to_hex(base_color)}, foreground color: {rgb_to_hex(black)})") tint_night = contrasted_color_maker_night.for_contrast(contrast_wcag, contrast_apca_night) if tint_night is None: raise ValueError(f"night mode contrast requirement cannot be met (WCAG: {contrast_wcag}, APCA: {-contrast_apca_night}, base color: {rgb_to_hex(base_color)}, foreground color: {rgb_to_hex(white)})") tints_light[tint_number] = tint_light tints_night[tint_number] = tint_night def make_swatch_bg(colors, fg_color): lines = [] for base_color_name, tints in colors.items(): if lines: lines.append('|-') else: lines.append('{|') for tint_number, color in tints.items(): name = f"{base_color_name}-{tint_number}" lines.append(f'| style="background:{rgb_to_hex(color)}" | <div style="color:{fg_color};padding:15px 5px">{name}</div>') lines.append("|}") return "\n".join(lines) def make_swatch_fg(colors, bg_color): lines = [] for base_color_name, tints in colors.items(): if lines: lines.append('|-') else: lines.append("{| " + f'style="background:{bg_color}"') for tint_number, color in tints.items(): name = f"{base_color_name}-{tint_number}" lines.append(f'| <div style="color:{rgb_to_hex(color)};padding:5px">{name}</div>') lines.append("|}") return "\n".join(lines) def make_unified_swatch_bg(colors): lines = [] for base_color_name, tints in colors.items(): if lines: lines.append('|-') else: lines.append("{| " + f'class="wikt-auto-contrast-exempt" style="color:var(--color-base)"') for tint_number, color in tints.items(): name = f"{base_color_name}-{tint_number}" lines.append(f'| style="background-color:var(--wikt-palette-{name},{rgb_to_hex(color)})" | <div style="padding:15px 5px">{name}</div>') lines.append("|}") return "\n".join(lines) def make_unified_swatch_fg(colors): lines = [] for base_color_name, tints in colors.items(): if lines: lines.append('|-') else: lines.append("{| " + f'class="wikt-auto-contrast-exempt" style="background:var(--background-color-base)"') for tint_number, color in tints.items(): name = f"{base_color_name}-{tint_number}" lines.append(f'| <div style="color:var(--wikt-palette-{name},{rgb_to_hex(color)});padding:5px">{name}</div>') lines.append("|}") return "\n".join(lines) def make_palette(colors_light, colors_night): def wrap_frame(prefix, indent, suffix): def _wrap(lines): return prefix + "\n" + "\n".join(indent + line for line in lines) + "\n" + suffix return _wrap def gather_colors(colors): rules = ["/* Autogenerated with " + IDENTIFICATION + " */"] for base_color_name, tints in colors.items(): for tint_number, color in tints.items(): name = f"{base_color_name}-{tint_number}" rules.append(f"--wikt-palette-{name}: {rgb_to_hex(color)};") return rules light_block = wrap_frame(":root, .skin-invert, .notheme {", "\t", "}") night_block_1 = wrap_frame("@media screen {\n\thtml.skin-theme-clientpref-night {", "\t\t", "\t}\n}") night_block_2 = wrap_frame("@media screen and (prefers-color-scheme: dark) {\n\thtml.skin-theme-clientpref-os {".strip(), "\t\t", "\t}\n}") rules_light = gather_colors(colors_light) rules_night = gather_colors(colors_night) return light_block(rules_light) + "\n\n/* Styles need to be duplicated exactly between these two selectors. */\n" + night_block_1(rules_night) + "\n" + night_block_2(rules_night) def print_swatches(): print("==Swatches==") print("") print("===Light mode===") print("<!-- Autogenerated with " + IDENTIFICATION + " */ -->") print(make_swatch_bg(colors_light, BLACK_FG)) print("") print(make_swatch_fg(colors_light, WHITE_BG)) print("") print("===Night mode===") print("<!-- Autogenerated with " + IDENTIFICATION + " */ -->") print(make_swatch_bg(colors_night, WHITE_FG)) print("") print(make_swatch_fg(colors_night, BLACK_BG)) def print_unified_swatch(): print("==Swatch==") print("Automatically adjusts for light/dark mode.") print("<!-- Autogenerated with " + IDENTIFICATION + " */ -->") print(make_unified_swatch_bg(colors_light)) print("") print(make_unified_swatch_fg(colors_light)) def print_palette(): print(make_palette(colors_light, colors_night)) if __name__ == "__main__": #print_swatches() print_unified_swatch() #print_palette() </syntaxhighlight> ==Swatch== Automatically adjusts for light/dark mode. <!-- Autogenerated with [[Wiktionary:Palette/numbered]] */ --> {| class="wikt-auto-contrast-exempt" style="color:var(--color-base)" | style="background-color:var(--wikt-palette-red-0,#fef9f9)" | <div style="padding:15px 5px">red-0</div> | style="background-color:var(--wikt-palette-red-1,#fdeef0)" | <div style="padding:15px 5px">red-1</div> | style="background-color:var(--wikt-palette-red-2,#fce4e6)" | <div style="padding:15px 5px">red-2</div> | style="background-color:var(--wikt-palette-red-3,#fad9dc)" | <div style="padding:15px 5px">red-3</div> | style="background-color:var(--wikt-palette-red-4,#f7c2c6)" | <div style="padding:15px 5px">red-4</div> | style="background-color:var(--wikt-palette-red-5,#f5afb5)" | <div style="padding:15px 5px">red-5</div> | style="background-color:var(--wikt-palette-red-6,#f29ba3)" | <div style="padding:15px 5px">red-6</div> | style="background-color:var(--wikt-palette-red-7,#f0858e)" | <div style="padding:15px 5px">red-7</div> | style="background-color:var(--wikt-palette-red-8,#ed6d78)" | <div style="padding:15px 5px">red-8</div> | style="background-color:var(--wikt-palette-red-9,#ce4350)" | <div style="padding:15px 5px">red-9</div> |- | style="background-color:var(--wikt-palette-scarlet-0,#fef9f7)" | <div style="padding:15px 5px">scarlet-0</div> | style="background-color:var(--wikt-palette-scarlet-1,#fcefeb)" | <div style="padding:15px 5px">scarlet-1</div> | style="background-color:var(--wikt-palette-scarlet-2,#fae5de)" | <div style="padding:15px 5px">scarlet-2</div> | style="background-color:var(--wikt-palette-scarlet-3,#f8dad1)" | <div style="padding:15px 5px">scarlet-3</div> | style="background-color:var(--wikt-palette-scarlet-4,#f4c4b5)" | <div style="padding:15px 5px">scarlet-4</div> | style="background-color:var(--wikt-palette-scarlet-5,#f1b3a0)" | <div style="padding:15px 5px">scarlet-5</div> | style="background-color:var(--wikt-palette-scarlet-6,#eea088)" | <div style="padding:15px 5px">scarlet-6</div> | style="background-color:var(--wikt-palette-scarlet-7,#ea8c70)" | <div style="padding:15px 5px">scarlet-7</div> | style="background-color:var(--wikt-palette-scarlet-8,#e67553)" | <div style="padding:15px 5px">scarlet-8</div> | style="background-color:var(--wikt-palette-scarlet-9,#c25433)" | <div style="padding:15px 5px">scarlet-9</div> |- | style="background-color:var(--wikt-palette-orange-0,#fdfaf5)" | <div style="padding:15px 5px">orange-0</div> | style="background-color:var(--wikt-palette-orange-1,#fbf0e4)" | <div style="padding:15px 5px">orange-1</div> | style="background-color:var(--wikt-palette-orange-2,#f8e7d4)" | <div style="padding:15px 5px">orange-2</div> | style="background-color:var(--wikt-palette-orange-3,#f5ddc3)" | <div style="padding:15px 5px">orange-3</div> | style="background-color:var(--wikt-palette-orange-4,#f0c89e)" | <div style="padding:15px 5px">orange-4</div> | style="background-color:var(--wikt-palette-orange-5,#ebb882)" | <div style="padding:15px 5px">orange-5</div> | style="background-color:var(--wikt-palette-orange-6,#e6a662)" | <div style="padding:15px 5px">orange-6</div> | style="background-color:var(--wikt-palette-orange-7,#e19341)" | <div style="padding:15px 5px">orange-7</div> | style="background-color:var(--wikt-palette-orange-8,#db7f1e)" | <div style="padding:15px 5px">orange-8</div> | style="background-color:var(--wikt-palette-orange-9,#b16210)" | <div style="padding:15px 5px">orange-9</div> |- | style="background-color:var(--wikt-palette-amber-0,#fdfaf3)" | <div style="padding:15px 5px">amber-0</div> | style="background-color:var(--wikt-palette-amber-1,#f9f1df)" | <div style="padding:15px 5px">amber-1</div> | style="background-color:var(--wikt-palette-amber-2,#f5e8cc)" | <div style="padding:15px 5px">amber-2</div> | style="background-color:var(--wikt-palette-amber-3,#f1dfb8)" | <div style="padding:15px 5px">amber-3</div> | style="background-color:var(--wikt-palette-amber-4,#e8cc8f)" | <div style="padding:15px 5px">amber-4</div> | style="background-color:var(--wikt-palette-amber-5,#e2bc6d)" | <div style="padding:15px 5px">amber-5</div> | style="background-color:var(--wikt-palette-amber-6,#dbac4a)" | <div style="padding:15px 5px">amber-6</div> | style="background-color:var(--wikt-palette-amber-7,#d39b24)" | <div style="padding:15px 5px">amber-7</div> | style="background-color:var(--wikt-palette-amber-8,#c38c17)" | <div style="padding:15px 5px">amber-8</div> | style="background-color:var(--wikt-palette-amber-9,#9b6f10)" | <div style="padding:15px 5px">amber-9</div> |- | style="background-color:var(--wikt-palette-yellow-0,#fbfbf4)" | <div style="padding:15px 5px">yellow-0</div> | style="background-color:var(--wikt-palette-yellow-1,#f5f2e0)" | <div style="padding:15px 5px">yellow-1</div> | style="background-color:var(--wikt-palette-yellow-2,#eeeacc)" | <div style="padding:15px 5px">yellow-2</div> | style="background-color:var(--wikt-palette-yellow-3,#e8e2b9)" | <div style="padding:15px 5px">yellow-3</div> | style="background-color:var(--wikt-palette-yellow-4,#dbd191)" | <div style="padding:15px 5px">yellow-4</div> | style="background-color:var(--wikt-palette-yellow-5,#d0c370)" | <div style="padding:15px 5px">yellow-5</div> | style="background-color:var(--wikt-palette-yellow-6,#c5b44f)" | <div style="padding:15px 5px">yellow-6</div> | style="background-color:var(--wikt-palette-yellow-7,#b6a63f)" | <div style="padding:15px 5px">yellow-7</div> | style="background-color:var(--wikt-palette-yellow-8,#a69737)" | <div style="padding:15px 5px">yellow-8</div> | style="background-color:var(--wikt-palette-yellow-9,#847827)" | <div style="padding:15px 5px">yellow-9</div> |- | style="background-color:var(--wikt-palette-lime-0,#f9fbf3)" | <div style="padding:15px 5px">lime-0</div> | style="background-color:var(--wikt-palette-lime-1,#eff4e1)" | <div style="padding:15px 5px">lime-1</div> | style="background-color:var(--wikt-palette-lime-2,#e5edce)" | <div style="padding:15px 5px">lime-2</div> | style="background-color:var(--wikt-palette-lime-3,#dbe5bc)" | <div style="padding:15px 5px">lime-3</div> | style="background-color:var(--wikt-palette-lime-4,#c5d693)" | <div style="padding:15px 5px">lime-4</div> | style="background-color:var(--wikt-palette-lime-5,#b5ca73)" | <div style="padding:15px 5px">lime-5</div> | style="background-color:var(--wikt-palette-lime-6,#a3be52)" | <div style="padding:15px 5px">lime-6</div> | style="background-color:var(--wikt-palette-lime-7,#97af4e)" | <div style="padding:15px 5px">lime-7</div> | style="background-color:var(--wikt-palette-lime-8,#889f43)" | <div style="padding:15px 5px">lime-8</div> | style="background-color:var(--wikt-palette-lime-9,#6b7f2f)" | <div style="padding:15px 5px">lime-9</div> |- | style="background-color:var(--wikt-palette-green-0,#f8fbf5)" | <div style="padding:15px 5px">green-0</div> | style="background-color:var(--wikt-palette-green-1,#eaf5e4)" | <div style="padding:15px 5px">green-1</div> | style="background-color:var(--wikt-palette-green-2,#ddefd3)" | <div style="padding:15px 5px">green-2</div> | style="background-color:var(--wikt-palette-green-3,#cee8c1)" | <div style="padding:15px 5px">green-3</div> | style="background-color:var(--wikt-palette-green-4,#b3da9d)" | <div style="padding:15px 5px">green-4</div> | style="background-color:var(--wikt-palette-green-5,#9cd080)" | <div style="padding:15px 5px">green-5</div> | style="background-color:var(--wikt-palette-green-6,#83c460)" | <div style="padding:15px 5px">green-6</div> | style="background-color:var(--wikt-palette-green-7,#69b840)" | <div style="padding:15px 5px">green-7</div> | style="background-color:var(--wikt-palette-green-8,#64a640)" | <div style="padding:15px 5px">green-8</div> | style="background-color:var(--wikt-palette-green-9,#4b852d)" | <div style="padding:15px 5px">green-9</div> |- | style="background-color:var(--wikt-palette-teal-0,#f6fcf9)" | <div style="padding:15px 5px">teal-0</div> | style="background-color:var(--wikt-palette-teal-1,#e5f6ee)" | <div style="padding:15px 5px">teal-1</div> | style="background-color:var(--wikt-palette-teal-2,#d5efe3)" | <div style="padding:15px 5px">teal-2</div> | style="background-color:var(--wikt-palette-teal-3,#c3e9d7)" | <div style="padding:15px 5px">teal-3</div> | style="background-color:var(--wikt-palette-teal-4,#9fdcc0)" | <div style="padding:15px 5px">teal-4</div> | style="background-color:var(--wikt-palette-teal-5,#82d1ac)" | <div style="padding:15px 5px">teal-5</div> | style="background-color:var(--wikt-palette-teal-6,#64c599)" | <div style="padding:15px 5px">teal-6</div> | style="background-color:var(--wikt-palette-teal-7,#41b982)" | <div style="padding:15px 5px">teal-7</div> | style="background-color:var(--wikt-palette-teal-8,#3fa878)" | <div style="padding:15px 5px">teal-8</div> | style="background-color:var(--wikt-palette-teal-9,#2c865d)" | <div style="padding:15px 5px">teal-9</div> |- | style="background-color:var(--wikt-palette-cyan-0,#f6fbfc)" | <div style="padding:15px 5px">cyan-0</div> | style="background-color:var(--wikt-palette-cyan-1,#e7f4f7)" | <div style="padding:15px 5px">cyan-1</div> | style="background-color:var(--wikt-palette-cyan-2,#d7edf1)" | <div style="padding:15px 5px">cyan-2</div> | style="background-color:var(--wikt-palette-cyan-3,#c7e6ec)" | <div style="padding:15px 5px">cyan-3</div> | style="background-color:var(--wikt-palette-cyan-4,#a6d7e0)" | <div style="padding:15px 5px">cyan-4</div> | style="background-color:var(--wikt-palette-cyan-5,#8accd7)" | <div style="padding:15px 5px">cyan-5</div> | style="background-color:var(--wikt-palette-cyan-6,#6ebfcd)" | <div style="padding:15px 5px">cyan-6</div> | style="background-color:var(--wikt-palette-cyan-7,#50b2c3)" | <div style="padding:15px 5px">cyan-7</div> | style="background-color:var(--wikt-palette-cyan-8,#2ea3b7)" | <div style="padding:15px 5px">cyan-8</div> | style="background-color:var(--wikt-palette-cyan-9,#288191)" | <div style="padding:15px 5px">cyan-9</div> |- | style="background-color:var(--wikt-palette-blue-0,#f8fafe)" | <div style="padding:15px 5px">blue-0</div> | style="background-color:var(--wikt-palette-blue-1,#ecf2fe)" | <div style="padding:15px 5px">blue-1</div> | style="background-color:var(--wikt-palette-blue-2,#e0eafd)" | <div style="padding:15px 5px">blue-2</div> | style="background-color:var(--wikt-palette-blue-3,#d3e1fc)" | <div style="padding:15px 5px">blue-3</div> | style="background-color:var(--wikt-palette-blue-4,#bbd0fa)" | <div style="padding:15px 5px">blue-4</div> | style="background-color:var(--wikt-palette-blue-5,#a6c2f9)" | <div style="padding:15px 5px">blue-5</div> | style="background-color:var(--wikt-palette-blue-6,#92b3f7)" | <div style="padding:15px 5px">blue-6</div> | style="background-color:var(--wikt-palette-blue-7,#7ca4f5)" | <div style="padding:15px 5px">blue-7</div> | style="background-color:var(--wikt-palette-blue-8,#6494f4)" | <div style="padding:15px 5px">blue-8</div> | style="background-color:var(--wikt-palette-blue-9,#3d72db)" | <div style="padding:15px 5px">blue-9</div> |- | style="background-color:var(--wikt-palette-indigo-0,#fafafe)" | <div style="padding:15px 5px">indigo-0</div> | style="background-color:var(--wikt-palette-indigo-1,#f1f1fd)" | <div style="padding:15px 5px">indigo-1</div> | style="background-color:var(--wikt-palette-indigo-2,#e9e7fc)" | <div style="padding:15px 5px">indigo-2</div> | style="background-color:var(--wikt-palette-indigo-3,#e0defa)" | <div style="padding:15px 5px">indigo-3</div> | style="background-color:var(--wikt-palette-indigo-4,#cecbf8)" | <div style="padding:15px 5px">indigo-4</div> | style="background-color:var(--wikt-palette-indigo-5,#bfbcf5)" | <div style="padding:15px 5px">indigo-5</div> | style="background-color:var(--wikt-palette-indigo-6,#b0acf3)" | <div style="padding:15px 5px">indigo-6</div> | style="background-color:var(--wikt-palette-indigo-7,#a19cf1)" | <div style="padding:15px 5px">indigo-7</div> | style="background-color:var(--wikt-palette-indigo-8,#918aef)" | <div style="padding:15px 5px">indigo-8</div> | style="background-color:var(--wikt-palette-indigo-9,#6e66e0)" | <div style="padding:15px 5px">indigo-9</div> |- | style="background-color:var(--wikt-palette-purple-0,#fcfafe)" | <div style="padding:15px 5px">purple-0</div> | style="background-color:var(--wikt-palette-purple-1,#f5f0fd)" | <div style="padding:15px 5px">purple-1</div> | style="background-color:var(--wikt-palette-purple-2,#efe6fc)" | <div style="padding:15px 5px">purple-2</div> | style="background-color:var(--wikt-palette-purple-3,#e8dbfb)" | <div style="padding:15px 5px">purple-3</div> | style="background-color:var(--wikt-palette-purple-4,#dbc6f8)" | <div style="padding:15px 5px">purple-4</div> | style="background-color:var(--wikt-palette-purple-5,#d0b6f7)" | <div style="padding:15px 5px">purple-5</div> | style="background-color:var(--wikt-palette-purple-6,#c5a5f5)" | <div style="padding:15px 5px">purple-6</div> | style="background-color:var(--wikt-palette-purple-7,#b993f2)" | <div style="padding:15px 5px">purple-7</div> | style="background-color:var(--wikt-palette-purple-8,#ad7ff0)" | <div style="padding:15px 5px">purple-8</div> | style="background-color:var(--wikt-palette-purple-9,#9053eb)" | <div style="padding:15px 5px">purple-9</div> |- | style="background-color:var(--wikt-palette-magenta-0,#fcf9fe)" | <div style="padding:15px 5px">magenta-0</div> | style="background-color:var(--wikt-palette-magenta-1,#f7effc)" | <div style="padding:15px 5px">magenta-1</div> | style="background-color:var(--wikt-palette-magenta-2,#f3e4fa)" | <div style="padding:15px 5px">magenta-2</div> | style="background-color:var(--wikt-palette-magenta-3,#eedaf9)" | <div style="padding:15px 5px">magenta-3</div> | style="background-color:var(--wikt-palette-magenta-4,#e3c4f5)" | <div style="padding:15px 5px">magenta-4</div> | style="background-color:var(--wikt-palette-magenta-5,#dbb2f2)" | <div style="padding:15px 5px">magenta-5</div> | style="background-color:var(--wikt-palette-magenta-6,#d3a0ef)" | <div style="padding:15px 5px">magenta-6</div> | style="background-color:var(--wikt-palette-magenta-7,#ca8deb)" | <div style="padding:15px 5px">magenta-7</div> | style="background-color:var(--wikt-palette-magenta-8,#c078e8)" | <div style="padding:15px 5px">magenta-8</div> | style="background-color:var(--wikt-palette-magenta-9,#a844df)" | <div style="padding:15px 5px">magenta-9</div> |- | style="background-color:var(--wikt-palette-rose-0,#fcf9fc)" | <div style="padding:15px 5px">rose-0</div> | style="background-color:var(--wikt-palette-rose-1,#f8f0f6)" | <div style="padding:15px 5px">rose-1</div> | style="background-color:var(--wikt-palette-rose-2,#f3e6ef)" | <div style="padding:15px 5px">rose-2</div> | style="background-color:var(--wikt-palette-rose-3,#eedbe9)" | <div style="padding:15px 5px">rose-3</div> | style="background-color:var(--wikt-palette-rose-4,#e4c7dc)" | <div style="padding:15px 5px">rose-4</div> | style="background-color:var(--wikt-palette-rose-5,#dcb6d2)" | <div style="padding:15px 5px">rose-5</div> | style="background-color:var(--wikt-palette-rose-6,#d4a5c8)" | <div style="padding:15px 5px">rose-6</div> | style="background-color:var(--wikt-palette-rose-7,#cc94bd)" | <div style="padding:15px 5px">rose-7</div> | style="background-color:var(--wikt-palette-rose-8,#c381b1)" | <div style="padding:15px 5px">rose-8</div> | style="background-color:var(--wikt-palette-rose-9,#af5797)" | <div style="padding:15px 5px">rose-9</div> |- | style="background-color:var(--wikt-palette-brown-0,#fbfaf9)" | <div style="padding:15px 5px">brown-0</div> | style="background-color:var(--wikt-palette-brown-1,#f4f1ee)" | <div style="padding:15px 5px">brown-1</div> | style="background-color:var(--wikt-palette-brown-2,#eee8e4)" | <div style="padding:15px 5px">brown-2</div> | style="background-color:var(--wikt-palette-brown-3,#e7dfd9)" | <div style="padding:15px 5px">brown-3</div> | style="background-color:var(--wikt-palette-brown-4,#d8cdc3)" | <div style="padding:15px 5px">brown-4</div> | style="background-color:var(--wikt-palette-brown-5,#cdbfb2)" | <div style="padding:15px 5px">brown-5</div> | style="background-color:var(--wikt-palette-brown-6,#c2b1a1)" | <div style="padding:15px 5px">brown-6</div> | style="background-color:var(--wikt-palette-brown-7,#b7a18f)" | <div style="padding:15px 5px">brown-7</div> | style="background-color:var(--wikt-palette-brown-8,#ab927c)" | <div style="padding:15px 5px">brown-8</div> | style="background-color:var(--wikt-palette-brown-9,#917155)" | <div style="padding:15px 5px">brown-9</div> |- | style="background-color:var(--wikt-palette-grey-red-0,#fef9f9)" | <div style="padding:15px 5px">grey-red-0</div> | style="background-color:var(--wikt-palette-grey-red-1,#fbeff0)" | <div style="padding:15px 5px">grey-red-1</div> | style="background-color:var(--wikt-palette-grey-red-2,#f9e5e6)" | <div style="padding:15px 5px">grey-red-2</div> | style="background-color:var(--wikt-palette-grey-red-3,#f7dadc)" | <div style="padding:15px 5px">grey-red-3</div> | style="background-color:var(--wikt-palette-grey-red-4,#f2c3c8)" | <div style="padding:15px 5px">grey-red-4</div> | style="background-color:var(--wikt-palette-grey-red-5,#eeb2b7)" | <div style="padding:15px 5px">grey-red-5</div> | style="background-color:var(--wikt-palette-grey-red-6,#dca5ab)" | <div style="padding:15px 5px">grey-red-6</div> | style="background-color:var(--wikt-palette-grey-red-7,#ce979c)" | <div style="padding:15px 5px">grey-red-7</div> | style="background-color:var(--wikt-palette-grey-red-8,#c0878c)" | <div style="padding:15px 5px">grey-red-8</div> | style="background-color:var(--wikt-palette-grey-red-9,#9f686d)" | <div style="padding:15px 5px">grey-red-9</div> |- | style="background-color:var(--wikt-palette-grey-scarlet-0,#fdfaf8)" | <div style="padding:15px 5px">grey-scarlet-0</div> | style="background-color:var(--wikt-palette-grey-scarlet-1,#faf0ec)" | <div style="padding:15px 5px">grey-scarlet-1</div> | style="background-color:var(--wikt-palette-grey-scarlet-2,#f7e6e0)" | <div style="padding:15px 5px">grey-scarlet-2</div> | style="background-color:var(--wikt-palette-grey-scarlet-3,#f4dcd4)" | <div style="padding:15px 5px">grey-scarlet-3</div> | style="background-color:var(--wikt-palette-grey-scarlet-4,#eec7bb)" | <div style="padding:15px 5px">grey-scarlet-4</div> | style="background-color:var(--wikt-palette-grey-scarlet-5,#e8b6a7)" | <div style="padding:15px 5px">grey-scarlet-5</div> | style="background-color:var(--wikt-palette-grey-scarlet-6,#d6a99c)" | <div style="padding:15px 5px">grey-scarlet-6</div> | style="background-color:var(--wikt-palette-grey-scarlet-7,#c89b8d)" | <div style="padding:15px 5px">grey-scarlet-7</div> | style="background-color:var(--wikt-palette-grey-scarlet-8,#b98c7e)" | <div style="padding:15px 5px">grey-scarlet-8</div> | style="background-color:var(--wikt-palette-grey-scarlet-9,#986d5f)" | <div style="padding:15px 5px">grey-scarlet-9</div> |- | style="background-color:var(--wikt-palette-grey-orange-0,#fdfaf6)" | <div style="padding:15px 5px">grey-orange-0</div> | style="background-color:var(--wikt-palette-grey-orange-1,#f9f0e8)" | <div style="padding:15px 5px">grey-orange-1</div> | style="background-color:var(--wikt-palette-grey-orange-2,#f4e7d9)" | <div style="padding:15px 5px">grey-orange-2</div> | style="background-color:var(--wikt-palette-grey-orange-3,#f0deca)" | <div style="padding:15px 5px">grey-orange-3</div> | style="background-color:var(--wikt-palette-grey-orange-4,#e8caaa)" | <div style="padding:15px 5px">grey-orange-4</div> | style="background-color:var(--wikt-palette-grey-orange-5,#e1bb92)" | <div style="padding:15px 5px">grey-orange-5</div> | style="background-color:var(--wikt-palette-grey-orange-6,#cfae8a)" | <div style="padding:15px 5px">grey-orange-6</div> | style="background-color:var(--wikt-palette-grey-orange-7,#c19f7c)" | <div style="padding:15px 5px">grey-orange-7</div> | style="background-color:var(--wikt-palette-grey-orange-8,#b2906d)" | <div style="padding:15px 5px">grey-orange-8</div> | style="background-color:var(--wikt-palette-grey-orange-9,#917151)" | <div style="padding:15px 5px">grey-orange-9</div> |- | style="background-color:var(--wikt-palette-grey-amber-0,#fcfaf5)" | <div style="padding:15px 5px">grey-amber-0</div> | style="background-color:var(--wikt-palette-grey-amber-1,#f7f1e6)" | <div style="padding:15px 5px">grey-amber-1</div> | style="background-color:var(--wikt-palette-grey-amber-2,#f1e9d6)" | <div style="padding:15px 5px">grey-amber-2</div> | style="background-color:var(--wikt-palette-grey-amber-3,#ece0c6)" | <div style="padding:15px 5px">grey-amber-3</div> | style="background-color:var(--wikt-palette-grey-amber-4,#e0cda3)" | <div style="padding:15px 5px">grey-amber-4</div> | style="background-color:var(--wikt-palette-grey-amber-5,#d8bf8a)" | <div style="padding:15px 5px">grey-amber-5</div> | style="background-color:var(--wikt-palette-grey-amber-6,#c6b185)" | <div style="padding:15px 5px">grey-amber-6</div> | style="background-color:var(--wikt-palette-grey-amber-7,#b7a377)" | <div style="padding:15px 5px">grey-amber-7</div> | style="background-color:var(--wikt-palette-grey-amber-8,#a89468)" | <div style="padding:15px 5px">grey-amber-8</div> | style="background-color:var(--wikt-palette-grey-amber-9,#88754c)" | <div style="padding:15px 5px">grey-amber-9</div> |- | style="background-color:var(--wikt-palette-grey-yellow-0,#fbfaf6)" | <div style="padding:15px 5px">grey-yellow-0</div> | style="background-color:var(--wikt-palette-grey-yellow-1,#f3f2e7)" | <div style="padding:15px 5px">grey-yellow-1</div> | style="background-color:var(--wikt-palette-grey-yellow-2,#ecead9)" | <div style="padding:15px 5px">grey-yellow-2</div> | style="background-color:var(--wikt-palette-grey-yellow-3,#e5e1ca)" | <div style="padding:15px 5px">grey-yellow-3</div> | style="background-color:var(--wikt-palette-grey-yellow-4,#d5d0ab)" | <div style="padding:15px 5px">grey-yellow-4</div> | style="background-color:var(--wikt-palette-grey-yellow-5,#cac394)" | <div style="padding:15px 5px">grey-yellow-5</div> | style="background-color:var(--wikt-palette-grey-yellow-6,#beb57c)" | <div style="padding:15px 5px">grey-yellow-6</div> | style="background-color:var(--wikt-palette-grey-yellow-7,#aca67f)" | <div style="padding:15px 5px">grey-yellow-7</div> | style="background-color:var(--wikt-palette-grey-yellow-8,#9e9770)" | <div style="padding:15px 5px">grey-yellow-8</div> | style="background-color:var(--wikt-palette-grey-yellow-9,#7e7851)" | <div style="padding:15px 5px">grey-yellow-9</div> |- | style="background-color:var(--wikt-palette-grey-lime-0,#fafbf7)" | <div style="padding:15px 5px">grey-lime-0</div> | style="background-color:var(--wikt-palette-grey-lime-1,#f0f3e8)" | <div style="padding:15px 5px">grey-lime-1</div> | style="background-color:var(--wikt-palette-grey-lime-2,#e7ebda)" | <div style="padding:15px 5px">grey-lime-2</div> | style="background-color:var(--wikt-palette-grey-lime-3,#dee3cc)" | <div style="padding:15px 5px">grey-lime-3</div> | style="background-color:var(--wikt-palette-grey-lime-4,#cad3ae)" | <div style="padding:15px 5px">grey-lime-4</div> | style="background-color:var(--wikt-palette-grey-lime-5,#bbc798)" | <div style="padding:15px 5px">grey-lime-5</div> | style="background-color:var(--wikt-palette-grey-lime-6,#abba81)" | <div style="padding:15px 5px">grey-lime-6</div> | style="background-color:var(--wikt-palette-grey-lime-7,#a0a984)" | <div style="padding:15px 5px">grey-lime-7</div> | style="background-color:var(--wikt-palette-grey-lime-8,#919b74)" | <div style="padding:15px 5px">grey-lime-8</div> | style="background-color:var(--wikt-palette-grey-lime-9,#727c55)" | <div style="padding:15px 5px">grey-lime-9</div> |- | style="background-color:var(--wikt-palette-grey-green-0,#f8fbf7)" | <div style="padding:15px 5px">grey-green-0</div> | style="background-color:var(--wikt-palette-grey-green-1,#edf4ea)" | <div style="padding:15px 5px">grey-green-1</div> | style="background-color:var(--wikt-palette-grey-green-2,#e2ecdd)" | <div style="padding:15px 5px">grey-green-2</div> | style="background-color:var(--wikt-palette-grey-green-3,#d6e5ce)" | <div style="padding:15px 5px">grey-green-3</div> | style="background-color:var(--wikt-palette-grey-green-4,#bfd6b3)" | <div style="padding:15px 5px">grey-green-4</div> | style="background-color:var(--wikt-palette-grey-green-5,#acca9d)" | <div style="padding:15px 5px">grey-green-5</div> | style="background-color:var(--wikt-palette-grey-green-6,#9abe87)" | <div style="padding:15px 5px">grey-green-6</div> | style="background-color:var(--wikt-palette-grey-green-7,#93ad86)" | <div style="padding:15px 5px">grey-green-7</div> | style="background-color:var(--wikt-palette-grey-green-8,#839e75)" | <div style="padding:15px 5px">grey-green-8</div> | style="background-color:var(--wikt-palette-grey-green-9,#647f56)" | <div style="padding:15px 5px">grey-green-9</div> |- | style="background-color:var(--wikt-palette-grey-teal-0,#f7fbf9)" | <div style="padding:15px 5px">grey-teal-0</div> | style="background-color:var(--wikt-palette-grey-teal-1,#eaf4ef)" | <div style="padding:15px 5px">grey-teal-1</div> | style="background-color:var(--wikt-palette-grey-teal-2,#ddede5)" | <div style="padding:15px 5px">grey-teal-2</div> | style="background-color:var(--wikt-palette-grey-teal-3,#d0e6dc)" | <div style="padding:15px 5px">grey-teal-3</div> | style="background-color:var(--wikt-palette-grey-teal-4,#b4d7c7)" | <div style="padding:15px 5px">grey-teal-4</div> | style="background-color:var(--wikt-palette-grey-teal-5,#9ecbb6)" | <div style="padding:15px 5px">grey-teal-5</div> | style="background-color:var(--wikt-palette-grey-teal-6,#88bfa5)" | <div style="padding:15px 5px">grey-teal-6</div> | style="background-color:var(--wikt-palette-grey-teal-7,#86ae9b)" | <div style="padding:15px 5px">grey-teal-7</div> | style="background-color:var(--wikt-palette-grey-teal-8,#769f8c)" | <div style="padding:15px 5px">grey-teal-8</div> | style="background-color:var(--wikt-palette-grey-teal-9,#56806d)" | <div style="padding:15px 5px">grey-teal-9</div> |- | style="background-color:var(--wikt-palette-grey-cyan-0,#f7fbfb)" | <div style="padding:15px 5px">grey-cyan-0</div> | style="background-color:var(--wikt-palette-grey-cyan-1,#ebf3f5)" | <div style="padding:15px 5px">grey-cyan-1</div> | style="background-color:var(--wikt-palette-grey-cyan-2,#deecee)" | <div style="padding:15px 5px">grey-cyan-2</div> | style="background-color:var(--wikt-palette-grey-cyan-3,#d1e4e7)" | <div style="padding:15px 5px">grey-cyan-3</div> | style="background-color:var(--wikt-palette-grey-cyan-4,#b6d4da)" | <div style="padding:15px 5px">grey-cyan-4</div> | style="background-color:var(--wikt-palette-grey-cyan-5,#a1c8cf)" | <div style="padding:15px 5px">grey-cyan-5</div> | style="background-color:var(--wikt-palette-grey-cyan-6,#8cbbc4)" | <div style="padding:15px 5px">grey-cyan-6</div> | style="background-color:var(--wikt-palette-grey-cyan-7,#86acb2)" | <div style="padding:15px 5px">grey-cyan-7</div> | style="background-color:var(--wikt-palette-grey-cyan-8,#769da3)" | <div style="padding:15px 5px">grey-cyan-8</div> | style="background-color:var(--wikt-palette-grey-cyan-9,#577e84)" | <div style="padding:15px 5px">grey-cyan-9</div> |- | style="background-color:var(--wikt-palette-grey-blue-0,#f8fafe)" | <div style="padding:15px 5px">grey-blue-0</div> | style="background-color:var(--wikt-palette-grey-blue-1,#ecf2fc)" | <div style="padding:15px 5px">grey-blue-1</div> | style="background-color:var(--wikt-palette-grey-blue-2,#e1eafb)" | <div style="padding:15px 5px">grey-blue-2</div> | style="background-color:var(--wikt-palette-grey-blue-3,#d5e1f9)" | <div style="padding:15px 5px">grey-blue-3</div> | style="background-color:var(--wikt-palette-grey-blue-4,#bdd0f6)" | <div style="padding:15px 5px">grey-blue-4</div> | style="background-color:var(--wikt-palette-grey-blue-5,#aec2ea)" | <div style="padding:15px 5px">grey-blue-5</div> | style="background-color:var(--wikt-palette-grey-blue-6,#9fb4dd)" | <div style="padding:15px 5px">grey-blue-6</div> | style="background-color:var(--wikt-palette-grey-blue-7,#91a6cf)" | <div style="padding:15px 5px">grey-blue-7</div> | style="background-color:var(--wikt-palette-grey-blue-8,#8297c0)" | <div style="padding:15px 5px">grey-blue-8</div> | style="background-color:var(--wikt-palette-grey-blue-9,#6477a0)" | <div style="padding:15px 5px">grey-blue-9</div> |- | style="background-color:var(--wikt-palette-grey-indigo-0,#fafafe)" | <div style="padding:15px 5px">grey-indigo-0</div> | style="background-color:var(--wikt-palette-grey-indigo-1,#f1f1fb)" | <div style="padding:15px 5px">grey-indigo-1</div> | style="background-color:var(--wikt-palette-grey-indigo-2,#e9e8f9)" | <div style="padding:15px 5px">grey-indigo-2</div> | style="background-color:var(--wikt-palette-grey-indigo-3,#e0def7)" | <div style="padding:15px 5px">grey-indigo-3</div> | style="background-color:var(--wikt-palette-grey-indigo-4,#cdcbf2)" | <div style="padding:15px 5px">grey-indigo-4</div> | style="background-color:var(--wikt-palette-grey-indigo-5,#c0bcee)" | <div style="padding:15px 5px">grey-indigo-5</div> | style="background-color:var(--wikt-palette-grey-indigo-6,#b2afdc)" | <div style="padding:15px 5px">grey-indigo-6</div> | style="background-color:var(--wikt-palette-grey-indigo-7,#a4a1d0)" | <div style="padding:15px 5px">grey-indigo-7</div> | style="background-color:var(--wikt-palette-grey-indigo-8,#9592c2)" | <div style="padding:15px 5px">grey-indigo-8</div> | style="background-color:var(--wikt-palette-grey-indigo-9,#7572a3)" | <div style="padding:15px 5px">grey-indigo-9</div> |- | style="background-color:var(--wikt-palette-grey-purple-0,#fbfafe)" | <div style="padding:15px 5px">grey-purple-0</div> | style="background-color:var(--wikt-palette-grey-purple-1,#f5f0fc)" | <div style="padding:15px 5px">grey-purple-1</div> | style="background-color:var(--wikt-palette-grey-purple-2,#eee6fa)" | <div style="padding:15px 5px">grey-purple-2</div> | style="background-color:var(--wikt-palette-grey-purple-3,#e7dcf8)" | <div style="padding:15px 5px">grey-purple-3</div> | style="background-color:var(--wikt-palette-grey-purple-4,#d9c8f3)" | <div style="padding:15px 5px">grey-purple-4</div> | style="background-color:var(--wikt-palette-grey-purple-5,#ceb8f0)" | <div style="padding:15px 5px">grey-purple-5</div> | style="background-color:var(--wikt-palette-grey-purple-6,#c0aae1)" | <div style="padding:15px 5px">grey-purple-6</div> | style="background-color:var(--wikt-palette-grey-purple-7,#b19bd3)" | <div style="padding:15px 5px">grey-purple-7</div> | style="background-color:var(--wikt-palette-grey-purple-8,#a38cc5)" | <div style="padding:15px 5px">grey-purple-8</div> | style="background-color:var(--wikt-palette-grey-purple-9,#846da5)" | <div style="padding:15px 5px">grey-purple-9</div> |- | style="background-color:var(--wikt-palette-grey-magenta-0,#fcf9fd)" | <div style="padding:15px 5px">grey-magenta-0</div> | style="background-color:var(--wikt-palette-grey-magenta-1,#f7effa)" | <div style="padding:15px 5px">grey-magenta-1</div> | style="background-color:var(--wikt-palette-grey-magenta-2,#f1e5f7)" | <div style="padding:15px 5px">grey-magenta-2</div> | style="background-color:var(--wikt-palette-grey-magenta-3,#ebdbf4)" | <div style="padding:15px 5px">grey-magenta-3</div> | style="background-color:var(--wikt-palette-grey-magenta-4,#e0c6ee)" | <div style="padding:15px 5px">grey-magenta-4</div> | style="background-color:var(--wikt-palette-grey-magenta-5,#d7b5e9)" | <div style="padding:15px 5px">grey-magenta-5</div> | style="background-color:var(--wikt-palette-grey-magenta-6,#cda4e4)" | <div style="padding:15px 5px">grey-magenta-6</div> | style="background-color:var(--wikt-palette-grey-magenta-7,#bc98d0)" | <div style="padding:15px 5px">grey-magenta-7</div> | style="background-color:var(--wikt-palette-grey-magenta-8,#ae89c2)" | <div style="padding:15px 5px">grey-magenta-8</div> | style="background-color:var(--wikt-palette-grey-magenta-9,#8e69a2)" | <div style="padding:15px 5px">grey-magenta-9</div> |- | style="background-color:var(--wikt-palette-grey-rose-0,#fcfafb)" | <div style="padding:15px 5px">grey-rose-0</div> | style="background-color:var(--wikt-palette-grey-rose-1,#f6f0f4)" | <div style="padding:15px 5px">grey-rose-1</div> | style="background-color:var(--wikt-palette-grey-rose-2,#efe7ed)" | <div style="padding:15px 5px">grey-rose-2</div> | style="background-color:var(--wikt-palette-grey-rose-3,#e9dde6)" | <div style="padding:15px 5px">grey-rose-3</div> | style="background-color:var(--wikt-palette-grey-rose-4,#ddcad8)" | <div style="padding:15px 5px">grey-rose-4</div> | style="background-color:var(--wikt-palette-grey-rose-5,#d3bacc)" | <div style="padding:15px 5px">grey-rose-5</div> | style="background-color:var(--wikt-palette-grey-rose-6,#c9abc1)" | <div style="padding:15px 5px">grey-rose-6</div> | style="background-color:var(--wikt-palette-grey-rose-7,#be9bb5)" | <div style="padding:15px 5px">grey-rose-7</div> | style="background-color:var(--wikt-palette-grey-rose-8,#b38aa8)" | <div style="padding:15px 5px">grey-rose-8</div> | style="background-color:var(--wikt-palette-grey-rose-9,#8c6e84)" | <div style="padding:15px 5px">grey-rose-9</div> |- | style="background-color:var(--wikt-palette-grey-brown-0,#fafaf9)" | <div style="padding:15px 5px">grey-brown-0</div> | style="background-color:var(--wikt-palette-grey-brown-1,#f3f2f0)" | <div style="padding:15px 5px">grey-brown-1</div> | style="background-color:var(--wikt-palette-grey-brown-2,#ebe9e6)" | <div style="padding:15px 5px">grey-brown-2</div> | style="background-color:var(--wikt-palette-grey-brown-3,#e4e0dc)" | <div style="padding:15px 5px">grey-brown-3</div> | style="background-color:var(--wikt-palette-grey-brown-4,#d4cec8)" | <div style="padding:15px 5px">grey-brown-4</div> | style="background-color:var(--wikt-palette-grey-brown-5,#c8c0b9)" | <div style="padding:15px 5px">grey-brown-5</div> | style="background-color:var(--wikt-palette-grey-brown-6,#bcb2a9)" | <div style="padding:15px 5px">grey-brown-6</div> | style="background-color:var(--wikt-palette-grey-brown-7,#afa398)" | <div style="padding:15px 5px">grey-brown-7</div> | style="background-color:var(--wikt-palette-grey-brown-8,#a19488)" | <div style="padding:15px 5px">grey-brown-8</div> | style="background-color:var(--wikt-palette-grey-brown-9,#7f766d)" | <div style="padding:15px 5px">grey-brown-9</div> |- | style="background-color:var(--wikt-palette-grey-0,#fafafa)" | <div style="padding:15px 5px">grey-0</div> | style="background-color:var(--wikt-palette-grey-1,#f2f2f2)" | <div style="padding:15px 5px">grey-1</div> | style="background-color:var(--wikt-palette-grey-2,#e9e9e9)" | <div style="padding:15px 5px">grey-2</div> | style="background-color:var(--wikt-palette-grey-3,#e1e1e1)" | <div style="padding:15px 5px">grey-3</div> | style="background-color:var(--wikt-palette-grey-4,#cfcfcf)" | <div style="padding:15px 5px">grey-4</div> | style="background-color:var(--wikt-palette-grey-5,#c2c2c2)" | <div style="padding:15px 5px">grey-5</div> | style="background-color:var(--wikt-palette-grey-6,#b4b4b4)" | <div style="padding:15px 5px">grey-6</div> | style="background-color:var(--wikt-palette-grey-7,#a5a5a5)" | <div style="padding:15px 5px">grey-7</div> | style="background-color:var(--wikt-palette-grey-8,#979797)" | <div style="padding:15px 5px">grey-8</div> | style="background-color:var(--wikt-palette-grey-9,#777777)" | <div style="padding:15px 5px">grey-9</div> |} {| class="wikt-auto-contrast-exempt" style="background:var(--background-color-base)" | <div style="color:var(--wikt-palette-red-0,#fef9f9);padding:5px">red-0</div> | <div style="color:var(--wikt-palette-red-1,#fdeef0);padding:5px">red-1</div> | <div style="color:var(--wikt-palette-red-2,#fce4e6);padding:5px">red-2</div> | <div style="color:var(--wikt-palette-red-3,#fad9dc);padding:5px">red-3</div> | <div style="color:var(--wikt-palette-red-4,#f7c2c6);padding:5px">red-4</div> | <div style="color:var(--wikt-palette-red-5,#f5afb5);padding:5px">red-5</div> | <div style="color:var(--wikt-palette-red-6,#f29ba3);padding:5px">red-6</div> | <div style="color:var(--wikt-palette-red-7,#f0858e);padding:5px">red-7</div> | <div style="color:var(--wikt-palette-red-8,#ed6d78);padding:5px">red-8</div> | <div style="color:var(--wikt-palette-red-9,#ce4350);padding:5px">red-9</div> |- | <div style="color:var(--wikt-palette-scarlet-0,#fef9f7);padding:5px">scarlet-0</div> | <div style="color:var(--wikt-palette-scarlet-1,#fcefeb);padding:5px">scarlet-1</div> | <div style="color:var(--wikt-palette-scarlet-2,#fae5de);padding:5px">scarlet-2</div> | <div style="color:var(--wikt-palette-scarlet-3,#f8dad1);padding:5px">scarlet-3</div> | <div style="color:var(--wikt-palette-scarlet-4,#f4c4b5);padding:5px">scarlet-4</div> | <div style="color:var(--wikt-palette-scarlet-5,#f1b3a0);padding:5px">scarlet-5</div> | <div style="color:var(--wikt-palette-scarlet-6,#eea088);padding:5px">scarlet-6</div> | <div style="color:var(--wikt-palette-scarlet-7,#ea8c70);padding:5px">scarlet-7</div> | <div style="color:var(--wikt-palette-scarlet-8,#e67553);padding:5px">scarlet-8</div> | <div style="color:var(--wikt-palette-scarlet-9,#c25433);padding:5px">scarlet-9</div> |- | <div style="color:var(--wikt-palette-orange-0,#fdfaf5);padding:5px">orange-0</div> | <div style="color:var(--wikt-palette-orange-1,#fbf0e4);padding:5px">orange-1</div> | <div style="color:var(--wikt-palette-orange-2,#f8e7d4);padding:5px">orange-2</div> | <div style="color:var(--wikt-palette-orange-3,#f5ddc3);padding:5px">orange-3</div> | <div style="color:var(--wikt-palette-orange-4,#f0c89e);padding:5px">orange-4</div> | <div style="color:var(--wikt-palette-orange-5,#ebb882);padding:5px">orange-5</div> | <div style="color:var(--wikt-palette-orange-6,#e6a662);padding:5px">orange-6</div> | <div style="color:var(--wikt-palette-orange-7,#e19341);padding:5px">orange-7</div> | <div style="color:var(--wikt-palette-orange-8,#db7f1e);padding:5px">orange-8</div> | <div style="color:var(--wikt-palette-orange-9,#b16210);padding:5px">orange-9</div> |- | <div style="color:var(--wikt-palette-amber-0,#fdfaf3);padding:5px">amber-0</div> | <div style="color:var(--wikt-palette-amber-1,#f9f1df);padding:5px">amber-1</div> | <div style="color:var(--wikt-palette-amber-2,#f5e8cc);padding:5px">amber-2</div> | <div style="color:var(--wikt-palette-amber-3,#f1dfb8);padding:5px">amber-3</div> | <div style="color:var(--wikt-palette-amber-4,#e8cc8f);padding:5px">amber-4</div> | <div style="color:var(--wikt-palette-amber-5,#e2bc6d);padding:5px">amber-5</div> | <div style="color:var(--wikt-palette-amber-6,#dbac4a);padding:5px">amber-6</div> | <div style="color:var(--wikt-palette-amber-7,#d39b24);padding:5px">amber-7</div> | <div style="color:var(--wikt-palette-amber-8,#c38c17);padding:5px">amber-8</div> | <div style="color:var(--wikt-palette-amber-9,#9b6f10);padding:5px">amber-9</div> |- | <div style="color:var(--wikt-palette-yellow-0,#fbfbf4);padding:5px">yellow-0</div> | <div style="color:var(--wikt-palette-yellow-1,#f5f2e0);padding:5px">yellow-1</div> | <div style="color:var(--wikt-palette-yellow-2,#eeeacc);padding:5px">yellow-2</div> | <div style="color:var(--wikt-palette-yellow-3,#e8e2b9);padding:5px">yellow-3</div> | <div style="color:var(--wikt-palette-yellow-4,#dbd191);padding:5px">yellow-4</div> | <div style="color:var(--wikt-palette-yellow-5,#d0c370);padding:5px">yellow-5</div> | <div style="color:var(--wikt-palette-yellow-6,#c5b44f);padding:5px">yellow-6</div> | <div style="color:var(--wikt-palette-yellow-7,#b6a63f);padding:5px">yellow-7</div> | <div style="color:var(--wikt-palette-yellow-8,#a69737);padding:5px">yellow-8</div> | <div style="color:var(--wikt-palette-yellow-9,#847827);padding:5px">yellow-9</div> |- | <div style="color:var(--wikt-palette-lime-0,#f9fbf3);padding:5px">lime-0</div> | <div style="color:var(--wikt-palette-lime-1,#eff4e1);padding:5px">lime-1</div> | <div style="color:var(--wikt-palette-lime-2,#e5edce);padding:5px">lime-2</div> | <div style="color:var(--wikt-palette-lime-3,#dbe5bc);padding:5px">lime-3</div> | <div style="color:var(--wikt-palette-lime-4,#c5d693);padding:5px">lime-4</div> | <div style="color:var(--wikt-palette-lime-5,#b5ca73);padding:5px">lime-5</div> | <div style="color:var(--wikt-palette-lime-6,#a3be52);padding:5px">lime-6</div> | <div style="color:var(--wikt-palette-lime-7,#97af4e);padding:5px">lime-7</div> | <div style="color:var(--wikt-palette-lime-8,#889f43);padding:5px">lime-8</div> | <div style="color:var(--wikt-palette-lime-9,#6b7f2f);padding:5px">lime-9</div> |- | <div style="color:var(--wikt-palette-green-0,#f8fbf5);padding:5px">green-0</div> | <div style="color:var(--wikt-palette-green-1,#eaf5e4);padding:5px">green-1</div> | <div style="color:var(--wikt-palette-green-2,#ddefd3);padding:5px">green-2</div> | <div style="color:var(--wikt-palette-green-3,#cee8c1);padding:5px">green-3</div> | <div style="color:var(--wikt-palette-green-4,#b3da9d);padding:5px">green-4</div> | <div style="color:var(--wikt-palette-green-5,#9cd080);padding:5px">green-5</div> | <div style="color:var(--wikt-palette-green-6,#83c460);padding:5px">green-6</div> | <div style="color:var(--wikt-palette-green-7,#69b840);padding:5px">green-7</div> | <div style="color:var(--wikt-palette-green-8,#64a640);padding:5px">green-8</div> | <div style="color:var(--wikt-palette-green-9,#4b852d);padding:5px">green-9</div> |- | <div style="color:var(--wikt-palette-teal-0,#f6fcf9);padding:5px">teal-0</div> | <div style="color:var(--wikt-palette-teal-1,#e5f6ee);padding:5px">teal-1</div> | <div style="color:var(--wikt-palette-teal-2,#d5efe3);padding:5px">teal-2</div> | <div style="color:var(--wikt-palette-teal-3,#c3e9d7);padding:5px">teal-3</div> | <div style="color:var(--wikt-palette-teal-4,#9fdcc0);padding:5px">teal-4</div> | <div style="color:var(--wikt-palette-teal-5,#82d1ac);padding:5px">teal-5</div> | <div style="color:var(--wikt-palette-teal-6,#64c599);padding:5px">teal-6</div> | <div style="color:var(--wikt-palette-teal-7,#41b982);padding:5px">teal-7</div> | <div style="color:var(--wikt-palette-teal-8,#3fa878);padding:5px">teal-8</div> | <div style="color:var(--wikt-palette-teal-9,#2c865d);padding:5px">teal-9</div> |- | <div style="color:var(--wikt-palette-cyan-0,#f6fbfc);padding:5px">cyan-0</div> | <div style="color:var(--wikt-palette-cyan-1,#e7f4f7);padding:5px">cyan-1</div> | <div style="color:var(--wikt-palette-cyan-2,#d7edf1);padding:5px">cyan-2</div> | <div style="color:var(--wikt-palette-cyan-3,#c7e6ec);padding:5px">cyan-3</div> | <div style="color:var(--wikt-palette-cyan-4,#a6d7e0);padding:5px">cyan-4</div> | <div style="color:var(--wikt-palette-cyan-5,#8accd7);padding:5px">cyan-5</div> | <div style="color:var(--wikt-palette-cyan-6,#6ebfcd);padding:5px">cyan-6</div> | <div style="color:var(--wikt-palette-cyan-7,#50b2c3);padding:5px">cyan-7</div> | <div style="color:var(--wikt-palette-cyan-8,#2ea3b7);padding:5px">cyan-8</div> | <div style="color:var(--wikt-palette-cyan-9,#288191);padding:5px">cyan-9</div> |- | <div style="color:var(--wikt-palette-blue-0,#f8fafe);padding:5px">blue-0</div> | <div style="color:var(--wikt-palette-blue-1,#ecf2fe);padding:5px">blue-1</div> | <div style="color:var(--wikt-palette-blue-2,#e0eafd);padding:5px">blue-2</div> | <div style="color:var(--wikt-palette-blue-3,#d3e1fc);padding:5px">blue-3</div> | <div style="color:var(--wikt-palette-blue-4,#bbd0fa);padding:5px">blue-4</div> | <div style="color:var(--wikt-palette-blue-5,#a6c2f9);padding:5px">blue-5</div> | <div style="color:var(--wikt-palette-blue-6,#92b3f7);padding:5px">blue-6</div> | <div style="color:var(--wikt-palette-blue-7,#7ca4f5);padding:5px">blue-7</div> | <div style="color:var(--wikt-palette-blue-8,#6494f4);padding:5px">blue-8</div> | <div style="color:var(--wikt-palette-blue-9,#3d72db);padding:5px">blue-9</div> |- | <div style="color:var(--wikt-palette-indigo-0,#fafafe);padding:5px">indigo-0</div> | <div style="color:var(--wikt-palette-indigo-1,#f1f1fd);padding:5px">indigo-1</div> | <div style="color:var(--wikt-palette-indigo-2,#e9e7fc);padding:5px">indigo-2</div> | <div style="color:var(--wikt-palette-indigo-3,#e0defa);padding:5px">indigo-3</div> | <div style="color:var(--wikt-palette-indigo-4,#cecbf8);padding:5px">indigo-4</div> | <div style="color:var(--wikt-palette-indigo-5,#bfbcf5);padding:5px">indigo-5</div> | <div style="color:var(--wikt-palette-indigo-6,#b0acf3);padding:5px">indigo-6</div> | <div style="color:var(--wikt-palette-indigo-7,#a19cf1);padding:5px">indigo-7</div> | <div style="color:var(--wikt-palette-indigo-8,#918aef);padding:5px">indigo-8</div> | <div style="color:var(--wikt-palette-indigo-9,#6e66e0);padding:5px">indigo-9</div> |- | <div style="color:var(--wikt-palette-purple-0,#fcfafe);padding:5px">purple-0</div> | <div style="color:var(--wikt-palette-purple-1,#f5f0fd);padding:5px">purple-1</div> | <div style="color:var(--wikt-palette-purple-2,#efe6fc);padding:5px">purple-2</div> | <div style="color:var(--wikt-palette-purple-3,#e8dbfb);padding:5px">purple-3</div> | <div style="color:var(--wikt-palette-purple-4,#dbc6f8);padding:5px">purple-4</div> | <div style="color:var(--wikt-palette-purple-5,#d0b6f7);padding:5px">purple-5</div> | <div style="color:var(--wikt-palette-purple-6,#c5a5f5);padding:5px">purple-6</div> | <div style="color:var(--wikt-palette-purple-7,#b993f2);padding:5px">purple-7</div> | <div style="color:var(--wikt-palette-purple-8,#ad7ff0);padding:5px">purple-8</div> | <div style="color:var(--wikt-palette-purple-9,#9053eb);padding:5px">purple-9</div> |- | <div style="color:var(--wikt-palette-magenta-0,#fcf9fe);padding:5px">magenta-0</div> | <div style="color:var(--wikt-palette-magenta-1,#f7effc);padding:5px">magenta-1</div> | <div style="color:var(--wikt-palette-magenta-2,#f3e4fa);padding:5px">magenta-2</div> | <div style="color:var(--wikt-palette-magenta-3,#eedaf9);padding:5px">magenta-3</div> | <div style="color:var(--wikt-palette-magenta-4,#e3c4f5);padding:5px">magenta-4</div> | <div style="color:var(--wikt-palette-magenta-5,#dbb2f2);padding:5px">magenta-5</div> | <div style="color:var(--wikt-palette-magenta-6,#d3a0ef);padding:5px">magenta-6</div> | <div style="color:var(--wikt-palette-magenta-7,#ca8deb);padding:5px">magenta-7</div> | <div style="color:var(--wikt-palette-magenta-8,#c078e8);padding:5px">magenta-8</div> | <div style="color:var(--wikt-palette-magenta-9,#a844df);padding:5px">magenta-9</div> |- | <div style="color:var(--wikt-palette-rose-0,#fcf9fc);padding:5px">rose-0</div> | <div style="color:var(--wikt-palette-rose-1,#f8f0f6);padding:5px">rose-1</div> | <div style="color:var(--wikt-palette-rose-2,#f3e6ef);padding:5px">rose-2</div> | <div style="color:var(--wikt-palette-rose-3,#eedbe9);padding:5px">rose-3</div> | <div style="color:var(--wikt-palette-rose-4,#e4c7dc);padding:5px">rose-4</div> | <div style="color:var(--wikt-palette-rose-5,#dcb6d2);padding:5px">rose-5</div> | <div style="color:var(--wikt-palette-rose-6,#d4a5c8);padding:5px">rose-6</div> | <div style="color:var(--wikt-palette-rose-7,#cc94bd);padding:5px">rose-7</div> | <div style="color:var(--wikt-palette-rose-8,#c381b1);padding:5px">rose-8</div> | <div style="color:var(--wikt-palette-rose-9,#af5797);padding:5px">rose-9</div> |- | <div style="color:var(--wikt-palette-brown-0,#fbfaf9);padding:5px">brown-0</div> | <div style="color:var(--wikt-palette-brown-1,#f4f1ee);padding:5px">brown-1</div> | <div style="color:var(--wikt-palette-brown-2,#eee8e4);padding:5px">brown-2</div> | <div style="color:var(--wikt-palette-brown-3,#e7dfd9);padding:5px">brown-3</div> | <div style="color:var(--wikt-palette-brown-4,#d8cdc3);padding:5px">brown-4</div> | <div style="color:var(--wikt-palette-brown-5,#cdbfb2);padding:5px">brown-5</div> | <div style="color:var(--wikt-palette-brown-6,#c2b1a1);padding:5px">brown-6</div> | <div style="color:var(--wikt-palette-brown-7,#b7a18f);padding:5px">brown-7</div> | <div style="color:var(--wikt-palette-brown-8,#ab927c);padding:5px">brown-8</div> | <div style="color:var(--wikt-palette-brown-9,#917155);padding:5px">brown-9</div> |- | <div style="color:var(--wikt-palette-grey-red-0,#fef9f9);padding:5px">grey-red-0</div> | <div style="color:var(--wikt-palette-grey-red-1,#fbeff0);padding:5px">grey-red-1</div> | <div style="color:var(--wikt-palette-grey-red-2,#f9e5e6);padding:5px">grey-red-2</div> | <div style="color:var(--wikt-palette-grey-red-3,#f7dadc);padding:5px">grey-red-3</div> | <div style="color:var(--wikt-palette-grey-red-4,#f2c3c8);padding:5px">grey-red-4</div> | <div style="color:var(--wikt-palette-grey-red-5,#eeb2b7);padding:5px">grey-red-5</div> | <div style="color:var(--wikt-palette-grey-red-6,#dca5ab);padding:5px">grey-red-6</div> | <div style="color:var(--wikt-palette-grey-red-7,#ce979c);padding:5px">grey-red-7</div> | <div style="color:var(--wikt-palette-grey-red-8,#c0878c);padding:5px">grey-red-8</div> | <div style="color:var(--wikt-palette-grey-red-9,#9f686d);padding:5px">grey-red-9</div> |- | <div style="color:var(--wikt-palette-grey-scarlet-0,#fdfaf8);padding:5px">grey-scarlet-0</div> | <div style="color:var(--wikt-palette-grey-scarlet-1,#faf0ec);padding:5px">grey-scarlet-1</div> | <div style="color:var(--wikt-palette-grey-scarlet-2,#f7e6e0);padding:5px">grey-scarlet-2</div> | <div style="color:var(--wikt-palette-grey-scarlet-3,#f4dcd4);padding:5px">grey-scarlet-3</div> | <div style="color:var(--wikt-palette-grey-scarlet-4,#eec7bb);padding:5px">grey-scarlet-4</div> | <div style="color:var(--wikt-palette-grey-scarlet-5,#e8b6a7);padding:5px">grey-scarlet-5</div> | <div style="color:var(--wikt-palette-grey-scarlet-6,#d6a99c);padding:5px">grey-scarlet-6</div> | <div style="color:var(--wikt-palette-grey-scarlet-7,#c89b8d);padding:5px">grey-scarlet-7</div> | <div style="color:var(--wikt-palette-grey-scarlet-8,#b98c7e);padding:5px">grey-scarlet-8</div> | <div style="color:var(--wikt-palette-grey-scarlet-9,#986d5f);padding:5px">grey-scarlet-9</div> |- | <div style="color:var(--wikt-palette-grey-orange-0,#fdfaf6);padding:5px">grey-orange-0</div> | <div style="color:var(--wikt-palette-grey-orange-1,#f9f0e8);padding:5px">grey-orange-1</div> | <div style="color:var(--wikt-palette-grey-orange-2,#f4e7d9);padding:5px">grey-orange-2</div> | <div style="color:var(--wikt-palette-grey-orange-3,#f0deca);padding:5px">grey-orange-3</div> | <div style="color:var(--wikt-palette-grey-orange-4,#e8caaa);padding:5px">grey-orange-4</div> | <div style="color:var(--wikt-palette-grey-orange-5,#e1bb92);padding:5px">grey-orange-5</div> | <div style="color:var(--wikt-palette-grey-orange-6,#cfae8a);padding:5px">grey-orange-6</div> | <div style="color:var(--wikt-palette-grey-orange-7,#c19f7c);padding:5px">grey-orange-7</div> | <div style="color:var(--wikt-palette-grey-orange-8,#b2906d);padding:5px">grey-orange-8</div> | <div style="color:var(--wikt-palette-grey-orange-9,#917151);padding:5px">grey-orange-9</div> |- | <div style="color:var(--wikt-palette-grey-amber-0,#fcfaf5);padding:5px">grey-amber-0</div> | <div style="color:var(--wikt-palette-grey-amber-1,#f7f1e6);padding:5px">grey-amber-1</div> | <div style="color:var(--wikt-palette-grey-amber-2,#f1e9d6);padding:5px">grey-amber-2</div> | <div style="color:var(--wikt-palette-grey-amber-3,#ece0c6);padding:5px">grey-amber-3</div> | <div style="color:var(--wikt-palette-grey-amber-4,#e0cda3);padding:5px">grey-amber-4</div> | <div style="color:var(--wikt-palette-grey-amber-5,#d8bf8a);padding:5px">grey-amber-5</div> | <div style="color:var(--wikt-palette-grey-amber-6,#c6b185);padding:5px">grey-amber-6</div> | <div style="color:var(--wikt-palette-grey-amber-7,#b7a377);padding:5px">grey-amber-7</div> | <div style="color:var(--wikt-palette-grey-amber-8,#a89468);padding:5px">grey-amber-8</div> | <div style="color:var(--wikt-palette-grey-amber-9,#88754c);padding:5px">grey-amber-9</div> |- | <div style="color:var(--wikt-palette-grey-yellow-0,#fbfaf6);padding:5px">grey-yellow-0</div> | <div style="color:var(--wikt-palette-grey-yellow-1,#f3f2e7);padding:5px">grey-yellow-1</div> | <div style="color:var(--wikt-palette-grey-yellow-2,#ecead9);padding:5px">grey-yellow-2</div> | <div style="color:var(--wikt-palette-grey-yellow-3,#e5e1ca);padding:5px">grey-yellow-3</div> | <div style="color:var(--wikt-palette-grey-yellow-4,#d5d0ab);padding:5px">grey-yellow-4</div> | <div style="color:var(--wikt-palette-grey-yellow-5,#cac394);padding:5px">grey-yellow-5</div> | <div style="color:var(--wikt-palette-grey-yellow-6,#beb57c);padding:5px">grey-yellow-6</div> | <div style="color:var(--wikt-palette-grey-yellow-7,#aca67f);padding:5px">grey-yellow-7</div> | <div style="color:var(--wikt-palette-grey-yellow-8,#9e9770);padding:5px">grey-yellow-8</div> | <div style="color:var(--wikt-palette-grey-yellow-9,#7e7851);padding:5px">grey-yellow-9</div> |- | <div style="color:var(--wikt-palette-grey-lime-0,#fafbf7);padding:5px">grey-lime-0</div> | <div style="color:var(--wikt-palette-grey-lime-1,#f0f3e8);padding:5px">grey-lime-1</div> | <div style="color:var(--wikt-palette-grey-lime-2,#e7ebda);padding:5px">grey-lime-2</div> | <div style="color:var(--wikt-palette-grey-lime-3,#dee3cc);padding:5px">grey-lime-3</div> | <div style="color:var(--wikt-palette-grey-lime-4,#cad3ae);padding:5px">grey-lime-4</div> | <div style="color:var(--wikt-palette-grey-lime-5,#bbc798);padding:5px">grey-lime-5</div> | <div style="color:var(--wikt-palette-grey-lime-6,#abba81);padding:5px">grey-lime-6</div> | <div style="color:var(--wikt-palette-grey-lime-7,#a0a984);padding:5px">grey-lime-7</div> | <div style="color:var(--wikt-palette-grey-lime-8,#919b74);padding:5px">grey-lime-8</div> | <div style="color:var(--wikt-palette-grey-lime-9,#727c55);padding:5px">grey-lime-9</div> |- | <div style="color:var(--wikt-palette-grey-green-0,#f8fbf7);padding:5px">grey-green-0</div> | <div style="color:var(--wikt-palette-grey-green-1,#edf4ea);padding:5px">grey-green-1</div> | <div style="color:var(--wikt-palette-grey-green-2,#e2ecdd);padding:5px">grey-green-2</div> | <div style="color:var(--wikt-palette-grey-green-3,#d6e5ce);padding:5px">grey-green-3</div> | <div style="color:var(--wikt-palette-grey-green-4,#bfd6b3);padding:5px">grey-green-4</div> | <div style="color:var(--wikt-palette-grey-green-5,#acca9d);padding:5px">grey-green-5</div> | <div style="color:var(--wikt-palette-grey-green-6,#9abe87);padding:5px">grey-green-6</div> | <div style="color:var(--wikt-palette-grey-green-7,#93ad86);padding:5px">grey-green-7</div> | <div style="color:var(--wikt-palette-grey-green-8,#839e75);padding:5px">grey-green-8</div> | <div style="color:var(--wikt-palette-grey-green-9,#647f56);padding:5px">grey-green-9</div> |- | <div style="color:var(--wikt-palette-grey-teal-0,#f7fbf9);padding:5px">grey-teal-0</div> | <div style="color:var(--wikt-palette-grey-teal-1,#eaf4ef);padding:5px">grey-teal-1</div> | <div style="color:var(--wikt-palette-grey-teal-2,#ddede5);padding:5px">grey-teal-2</div> | <div style="color:var(--wikt-palette-grey-teal-3,#d0e6dc);padding:5px">grey-teal-3</div> | <div style="color:var(--wikt-palette-grey-teal-4,#b4d7c7);padding:5px">grey-teal-4</div> | <div style="color:var(--wikt-palette-grey-teal-5,#9ecbb6);padding:5px">grey-teal-5</div> | <div style="color:var(--wikt-palette-grey-teal-6,#88bfa5);padding:5px">grey-teal-6</div> | <div style="color:var(--wikt-palette-grey-teal-7,#86ae9b);padding:5px">grey-teal-7</div> | <div style="color:var(--wikt-palette-grey-teal-8,#769f8c);padding:5px">grey-teal-8</div> | <div style="color:var(--wikt-palette-grey-teal-9,#56806d);padding:5px">grey-teal-9</div> |- | <div style="color:var(--wikt-palette-grey-cyan-0,#f7fbfb);padding:5px">grey-cyan-0</div> | <div style="color:var(--wikt-palette-grey-cyan-1,#ebf3f5);padding:5px">grey-cyan-1</div> | <div style="color:var(--wikt-palette-grey-cyan-2,#deecee);padding:5px">grey-cyan-2</div> | <div style="color:var(--wikt-palette-grey-cyan-3,#d1e4e7);padding:5px">grey-cyan-3</div> | <div style="color:var(--wikt-palette-grey-cyan-4,#b6d4da);padding:5px">grey-cyan-4</div> | <div style="color:var(--wikt-palette-grey-cyan-5,#a1c8cf);padding:5px">grey-cyan-5</div> | <div style="color:var(--wikt-palette-grey-cyan-6,#8cbbc4);padding:5px">grey-cyan-6</div> | <div style="color:var(--wikt-palette-grey-cyan-7,#86acb2);padding:5px">grey-cyan-7</div> | <div style="color:var(--wikt-palette-grey-cyan-8,#769da3);padding:5px">grey-cyan-8</div> | <div style="color:var(--wikt-palette-grey-cyan-9,#577e84);padding:5px">grey-cyan-9</div> |- | <div style="color:var(--wikt-palette-grey-blue-0,#f8fafe);padding:5px">grey-blue-0</div> | <div style="color:var(--wikt-palette-grey-blue-1,#ecf2fc);padding:5px">grey-blue-1</div> | <div style="color:var(--wikt-palette-grey-blue-2,#e1eafb);padding:5px">grey-blue-2</div> | <div style="color:var(--wikt-palette-grey-blue-3,#d5e1f9);padding:5px">grey-blue-3</div> | <div style="color:var(--wikt-palette-grey-blue-4,#bdd0f6);padding:5px">grey-blue-4</div> | <div style="color:var(--wikt-palette-grey-blue-5,#aec2ea);padding:5px">grey-blue-5</div> | <div style="color:var(--wikt-palette-grey-blue-6,#9fb4dd);padding:5px">grey-blue-6</div> | <div style="color:var(--wikt-palette-grey-blue-7,#91a6cf);padding:5px">grey-blue-7</div> | <div style="color:var(--wikt-palette-grey-blue-8,#8297c0);padding:5px">grey-blue-8</div> | <div style="color:var(--wikt-palette-grey-blue-9,#6477a0);padding:5px">grey-blue-9</div> |- | <div style="color:var(--wikt-palette-grey-indigo-0,#fafafe);padding:5px">grey-indigo-0</div> | <div style="color:var(--wikt-palette-grey-indigo-1,#f1f1fb);padding:5px">grey-indigo-1</div> | <div style="color:var(--wikt-palette-grey-indigo-2,#e9e8f9);padding:5px">grey-indigo-2</div> | <div style="color:var(--wikt-palette-grey-indigo-3,#e0def7);padding:5px">grey-indigo-3</div> | <div style="color:var(--wikt-palette-grey-indigo-4,#cdcbf2);padding:5px">grey-indigo-4</div> | <div style="color:var(--wikt-palette-grey-indigo-5,#c0bcee);padding:5px">grey-indigo-5</div> | <div style="color:var(--wikt-palette-grey-indigo-6,#b2afdc);padding:5px">grey-indigo-6</div> | <div style="color:var(--wikt-palette-grey-indigo-7,#a4a1d0);padding:5px">grey-indigo-7</div> | <div style="color:var(--wikt-palette-grey-indigo-8,#9592c2);padding:5px">grey-indigo-8</div> | <div style="color:var(--wikt-palette-grey-indigo-9,#7572a3);padding:5px">grey-indigo-9</div> |- | <div style="color:var(--wikt-palette-grey-purple-0,#fbfafe);padding:5px">grey-purple-0</div> | <div style="color:var(--wikt-palette-grey-purple-1,#f5f0fc);padding:5px">grey-purple-1</div> | <div style="color:var(--wikt-palette-grey-purple-2,#eee6fa);padding:5px">grey-purple-2</div> | <div style="color:var(--wikt-palette-grey-purple-3,#e7dcf8);padding:5px">grey-purple-3</div> | <div style="color:var(--wikt-palette-grey-purple-4,#d9c8f3);padding:5px">grey-purple-4</div> | <div style="color:var(--wikt-palette-grey-purple-5,#ceb8f0);padding:5px">grey-purple-5</div> | <div style="color:var(--wikt-palette-grey-purple-6,#c0aae1);padding:5px">grey-purple-6</div> | <div style="color:var(--wikt-palette-grey-purple-7,#b19bd3);padding:5px">grey-purple-7</div> | <div style="color:var(--wikt-palette-grey-purple-8,#a38cc5);padding:5px">grey-purple-8</div> | <div style="color:var(--wikt-palette-grey-purple-9,#846da5);padding:5px">grey-purple-9</div> |- | <div style="color:var(--wikt-palette-grey-magenta-0,#fcf9fd);padding:5px">grey-magenta-0</div> | <div style="color:var(--wikt-palette-grey-magenta-1,#f7effa);padding:5px">grey-magenta-1</div> | <div style="color:var(--wikt-palette-grey-magenta-2,#f1e5f7);padding:5px">grey-magenta-2</div> | <div style="color:var(--wikt-palette-grey-magenta-3,#ebdbf4);padding:5px">grey-magenta-3</div> | <div style="color:var(--wikt-palette-grey-magenta-4,#e0c6ee);padding:5px">grey-magenta-4</div> | <div style="color:var(--wikt-palette-grey-magenta-5,#d7b5e9);padding:5px">grey-magenta-5</div> | <div style="color:var(--wikt-palette-grey-magenta-6,#cda4e4);padding:5px">grey-magenta-6</div> | <div style="color:var(--wikt-palette-grey-magenta-7,#bc98d0);padding:5px">grey-magenta-7</div> | <div style="color:var(--wikt-palette-grey-magenta-8,#ae89c2);padding:5px">grey-magenta-8</div> | <div style="color:var(--wikt-palette-grey-magenta-9,#8e69a2);padding:5px">grey-magenta-9</div> |- | <div style="color:var(--wikt-palette-grey-rose-0,#fcfafb);padding:5px">grey-rose-0</div> | <div style="color:var(--wikt-palette-grey-rose-1,#f6f0f4);padding:5px">grey-rose-1</div> | <div style="color:var(--wikt-palette-grey-rose-2,#efe7ed);padding:5px">grey-rose-2</div> | <div style="color:var(--wikt-palette-grey-rose-3,#e9dde6);padding:5px">grey-rose-3</div> | <div style="color:var(--wikt-palette-grey-rose-4,#ddcad8);padding:5px">grey-rose-4</div> | <div style="color:var(--wikt-palette-grey-rose-5,#d3bacc);padding:5px">grey-rose-5</div> | <div style="color:var(--wikt-palette-grey-rose-6,#c9abc1);padding:5px">grey-rose-6</div> | <div style="color:var(--wikt-palette-grey-rose-7,#be9bb5);padding:5px">grey-rose-7</div> | <div style="color:var(--wikt-palette-grey-rose-8,#b38aa8);padding:5px">grey-rose-8</div> | <div style="color:var(--wikt-palette-grey-rose-9,#8c6e84);padding:5px">grey-rose-9</div> |- | <div style="color:var(--wikt-palette-grey-brown-0,#fafaf9);padding:5px">grey-brown-0</div> | <div style="color:var(--wikt-palette-grey-brown-1,#f3f2f0);padding:5px">grey-brown-1</div> | <div style="color:var(--wikt-palette-grey-brown-2,#ebe9e6);padding:5px">grey-brown-2</div> | <div style="color:var(--wikt-palette-grey-brown-3,#e4e0dc);padding:5px">grey-brown-3</div> | <div style="color:var(--wikt-palette-grey-brown-4,#d4cec8);padding:5px">grey-brown-4</div> | <div style="color:var(--wikt-palette-grey-brown-5,#c8c0b9);padding:5px">grey-brown-5</div> | <div style="color:var(--wikt-palette-grey-brown-6,#bcb2a9);padding:5px">grey-brown-6</div> | <div style="color:var(--wikt-palette-grey-brown-7,#afa398);padding:5px">grey-brown-7</div> | <div style="color:var(--wikt-palette-grey-brown-8,#a19488);padding:5px">grey-brown-8</div> | <div style="color:var(--wikt-palette-grey-brown-9,#7f766d);padding:5px">grey-brown-9</div> |- | <div style="color:var(--wikt-palette-grey-0,#fafafa);padding:5px">grey-0</div> | <div style="color:var(--wikt-palette-grey-1,#f2f2f2);padding:5px">grey-1</div> | <div style="color:var(--wikt-palette-grey-2,#e9e9e9);padding:5px">grey-2</div> | <div style="color:var(--wikt-palette-grey-3,#e1e1e1);padding:5px">grey-3</div> | <div style="color:var(--wikt-palette-grey-4,#cfcfcf);padding:5px">grey-4</div> | <div style="color:var(--wikt-palette-grey-5,#c2c2c2);padding:5px">grey-5</div> | <div style="color:var(--wikt-palette-grey-6,#b4b4b4);padding:5px">grey-6</div> | <div style="color:var(--wikt-palette-grey-7,#a5a5a5);padding:5px">grey-7</div> | <div style="color:var(--wikt-palette-grey-8,#979797);padding:5px">grey-8</div> | <div style="color:var(--wikt-palette-grey-9,#777777);padding:5px">grey-9</div> |} c8qjaobhznfjgywibx5c10d3sfm0467 Modul:palette 828 1358187 6291603 2026-07-26T20:16:56Z Wikihez 7344 [[:en:Module:palette]] hat kopîkirin ([[Bikarhêner:Wikihez/skrîpt/js/kopi-enWikt.js|kopi-enWikt.js]]) 6291603 Scribunto text/plain local export = {} -- Given a CSS hex value, return a table of the RGB components. -- The RGB is normalized so that white is (1.0, 1.0, 1.0). local function get_rgb_from_hex(hex_colour) hex_colour = hex_colour:gsub("#", "") if #hex_colour == 3 then return { tonumber(hex_colour:sub(1, 1), 16) * 17 / 255, tonumber(hex_colour:sub(2, 2), 16) * 17 / 255, tonumber(hex_colour:sub(3, 3), 16) * 17 / 255 } elseif #hex_colour == 6 then return { tonumber(hex_colour:sub(1, 2), 16) / 255, tonumber(hex_colour:sub(3, 4), 16) / 255, tonumber(hex_colour:sub(5, 6), 16) / 255 } end end -- Calculate the WCAG2.1 relative luminance value given a RGB tuple. -- https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html#dfn-relative-luminance local function get_WCAG_21_luminance(rgb) local luminance_parts = {} for _, rgb_part in ipairs(rgb) do if rgb_part > 0.04045 then table.insert(luminance_parts, ((rgb_part + 0.055) / 1.055) ^ 2.4) else table.insert(luminance_parts, rgb_part / 12.92) end end local luminance = 0.2126 * luminance_parts[1] + 0.7152 * luminance_parts[2] + 0.0722 * luminance_parts[3] return luminance end -- Calculate the WCAG2.1 contrast ratio given two RGB tuples. -- https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html#dfn-contrast-ratio local function get_WCAG_21_contrast(rgb1, rgb2) local luminance1 = get_WCAG_21_luminance(rgb1) local luminance2 = get_WCAG_21_luminance(rgb2) -- The lighter colour is used in the numerator. local contrast_ratio if luminance1 >= luminance2 then contrast_ratio = (luminance1 + 0.05) / (luminance2 + 0.05) else contrast_ratio = (luminance2 + 0.05) / (luminance1 + 0.05) end return contrast_ratio end -- Calculate the APCA contrast ratio given a text RGB tuple and a background RGB tuple. -- https://github.com/Myndex/SAPC-APCA/blob/master/documentation/APCA-W3-LaTeX.md -- The variables are named to match the LaTeX document. local function get_APCA_contrast(rgb_txt, rgb_bg) local Ys_txt = rgb_txt[1] ^ 2.4 * 0.2126729 + rgb_txt[2] ^ 2.4 * 0.7151522 + rgb_txt[3] ^ 2.4 * 0.0721750 local Ys_bg = rgb_bg[1] ^ 2.4 * 0.2126729 + rgb_bg[2] ^ 2.4 * 0.7151522 + rgb_bg[3] ^ 2.4 * 0.0721750 local function f_sc(Y_c) if Y_c <= 0 then return 0 elseif Y_c <= 0.022 then return Y_c + (0.022 - Y_c) ^ 1.414 else return Y_c end end local Y_txt = f_sc(Ys_txt) local Y_bg = f_sc(Ys_bg) local S_apc if Y_bg > Y_txt then S_apc = (Y_bg ^ 0.56 - Y_txt ^ 0.57) * 1.14 else S_apc = (Y_bg ^ 0.65 - Y_txt ^ 0.62) * 1.14 end local L_c if math.abs(S_apc) < 0.1 then L_c = 0 elseif S_apc > 0 then L_c = (S_apc - 0.027) * 100 else L_c = (S_apc + 0.027) * 100 end return L_c end -- Get the WCAG2.1 rating as HTML, given a contrast value. local function get_WCAG_21_rating(contrast_value) if contrast_value >= 7 then return "<span style=\"color:var(--wikt-palette-deepblue)\">(AAA)</span>" elseif contrast_value >= 4.5 then return "<span style=\"color:var(--wikt-palette-forestgreen)\">(AA)</span>" else return "<span style=\"color:var(--wikt-palette-red)\">(FAIL)</span>" end end -- Get the APCA rating as HTML, given a contrast value. -- Using default text values: font size = 14px, font weight = 400 local function get_APCA_rating(contrast_value) local abs_contrast_value = math.abs(contrast_value) if abs_contrast_value >= 100 then return "<span style=\"color:var(--wikt-palette-deepblue)\">(R4)</span>" elseif abs_contrast_value >= 95 then return "<span style=\"color:var(--wikt-palette-forestgreen)\">(R3)</span>" elseif abs_contrast_value >= 90 then return "<span style=\"color:var(--wikt-palette-forestgreen)\">(R2)</span>" elseif abs_contrast_value >= 85 then return "<span style=\"color:var(--wikt-palette-forestgreen)\">(R1)</span>" else return "<span style=\"color:var(--wikt-palette-red)\">(R0)</span>" end end function export.show(frame) local pageContent = mw.title.new("MediaWiki:Gadget-Palette.css"):getContent() local lightMode = true; local lightModeData = {} local darkModeData = {} local commentsData = {} local seenVariables = {} -- In order to maintain the order. local output = { [=[{| class="palette-table wikitable mw-no-invert" |+Table of palette variables in light and dark mode |- !scope="col"| Variable name !scope="col"| Light mode !scope="col"| Dark mode !scope="col"| Use as a background colour? !scope="col"| Use as a text colour? !scope="col"| Comments |-" ]=]} for line in pageContent:gmatch("[^\r\n]*") do -- Hack to check whether we've reached the dark mode styles. if line == "@media screen {" then lightMode = false; end local definedVariable = line:match("(--wikt%-palette%-[a-z0-9-]+):") if definedVariable then if lightMode then table.insert(seenVariables, definedVariable) lightModeData[definedVariable] = line:match("#[a-zA-Z0-9]+") commentsData[definedVariable] = line:match("/%*%s*(.-)%s*%*/") or "" else darkModeData[definedVariable] = line:match("#[a-zA-Z0-9]+") end end end for _, var in ipairs(seenVariables) do table.insert(output, "|<code>" .. var .. "</code>") table.insert(output, "||style=\"background:" .. lightModeData[var] .. "\"|<span class=\"palette-highlight\">" .. lightModeData[var] .. "</span>") table.insert(output, "||style=\"background:" .. darkModeData[var] .. "\"|<span class=\"palette-highlight\">" .. darkModeData[var] .. "</span>") -- Contrast on body text in light mode. local WCAG_21_contrast_text_lightmode = get_WCAG_21_contrast(get_rgb_from_hex("#202122"), get_rgb_from_hex(lightModeData[var])) local APCA_contrast_text_lightmode = get_APCA_contrast(get_rgb_from_hex("#202122"), get_rgb_from_hex(lightModeData[var])) table.insert(output, "||'''Light mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_text_lightmode) .. " " .. get_WCAG_21_rating(WCAG_21_contrast_text_lightmode)) table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_text_lightmode) .. " " .. get_APCA_rating(APCA_contrast_text_lightmode)) -- Contrast on body text in dark mode. local WCAG_21_contrast_text_darkmode = get_WCAG_21_contrast(get_rgb_from_hex("#eaecf0"), get_rgb_from_hex(darkModeData[var])) local APCA_contrast_text_darkmode = get_APCA_contrast(get_rgb_from_hex("#eaecf0"), get_rgb_from_hex(darkModeData[var])) table.insert(output, "<br> '''Dark mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_text_darkmode) .. " " .. get_WCAG_21_rating(WCAG_21_contrast_text_darkmode)) table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_text_darkmode) .. " " .. get_APCA_rating(APCA_contrast_text_darkmode)) -- Contrast on default background in light mode. local WCAG_21_contrast_bg_lightmode = get_WCAG_21_contrast(get_rgb_from_hex(lightModeData[var]), get_rgb_from_hex("#ffffff")) local APCA_contrast_bg_lightmode = get_APCA_contrast(get_rgb_from_hex(lightModeData[var]), get_rgb_from_hex("#ffffff")) table.insert(output, "||'''Light mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_bg_lightmode) .. " " .. get_WCAG_21_rating(WCAG_21_contrast_bg_lightmode)) table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_bg_lightmode) .. " " .. get_APCA_rating(APCA_contrast_bg_lightmode)) -- Contrast on default background in dark mode. local WCAG_21_contrast_bg_darkmode = get_WCAG_21_contrast(get_rgb_from_hex(darkModeData[var]), get_rgb_from_hex("#101418")) local APCA_contrast_bg_darkmode = get_APCA_contrast(get_rgb_from_hex(darkModeData[var]), get_rgb_from_hex("#101418")) table.insert(output, "<br> '''Dark mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_bg_darkmode) .. " " .. get_WCAG_21_rating(WCAG_21_contrast_bg_darkmode)) table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_bg_darkmode) .. " " .. get_APCA_rating(APCA_contrast_bg_darkmode)) -- Comments. table.insert(output, "||" .. commentsData[var]) table.insert(output, "\n|-\n") end table.insert(output, "|-\n") table.insert(output, "|colspan=\"6\" style=\"padding:1em;background:var(--wikt-palette-lavender,#f8f8ff)\"|According to [[mw:Recommendations for night mode compatibility on Wikimedia wikis]], editors should ensure that colours meet the WCAC 2.1 AA contrast standards (APCA is significantly stricter but corresponds more closely to human visual perception — see [[phab:T308772]]). Contrast ratios are calculated assuming standard text formatting (14px, unbolded) and standard colours on the rest of the page. To check contrast values between any two colours, see https://contrast.tools/.\n") table.insert(output, "\n|}") table.insert(output, frame:extensionTag("templatestyles", "", {src="Module:palette/styles.css"})) return table.concat(output) end return export l4m388x9nzb3p0b550nabnszjk5eux4 Modul:palette/belge 828 1358188 6291604 2026-07-26T20:16:57Z Wikihez 7344 [[:en:Module:palette/documentation]] hat kopîkirin ([[Bikarhêner:Wikihez/skrîpt/js/kopi-enWikt.js|kopi-enWikt.js]]) 6291604 wikitext text/x-wiki Generates a table of [[MediaWiki:Gadget-Palette.css|Palette]] variables along with contrast information. See [[MediaWiki:Gadget-Palette/table]]. b1fx0wwaqrxvqxpa1hbe49ty83du4pb MediaWiki:Gadget-Palette/table 8 1358189 6291605 2026-07-26T20:18:50Z Wikihez 7344 Rûpel bi "The Palette variables shown below are defined in [[MediaWiki:Gadget-Palette.css]]. To request a new variable to be added, do so [[MediaWiki talk:Gadget-Palette|here]] using this format: <code><name> <light mode hex code> <dark mode hex code></code>. See [[Wiktionary:Palette]] for more information. {{#invoke:palette|show}}" hat çêkirin 6291605 wikitext text/x-wiki The Palette variables shown below are defined in [[MediaWiki:Gadget-Palette.css]]. To request a new variable to be added, do so [[MediaWiki talk:Gadget-Palette|here]] using this format: <code><name> <light mode hex code> <dark mode hex code></code>. See [[Wiktionary:Palette]] for more information. {{#invoke:palette|show}} ibrl7ml1mx496004xkqd3btqmrqgn3k Modul:palette/styles.css 828 1358190 6291606 2026-07-26T20:20:09Z Wikihez 7344 [[:en:Module:palette/styles.css]] hat kopîkirin ([[Bikarhêner:Wikihez/skrîpt/js/kopi-enWikt.js|kopi-enWikt.js]]) 6291606 sanitized-css text/css .palette-table td:not(:first-child) { text-align: center; } .palette-table td:nth-child(4), .palette-table td:nth-child(5) { min-width: 200px; } .palette-highlight { position: relative; z-index: 0; padding: 2px 5px; background: var(--wikt-palette-lavender); opacity: 0.8; border-radius: 3px; } r22pfpm1zg2w8e1xod26e3llyprz737