विकिपीडिया
bhwiki
https://bh.wikipedia.org/wiki/%E0%A4%AE%E0%A5%81%E0%A4%96%E0%A5%8D%E0%A4%AF_%E0%A4%AA%E0%A4%A8%E0%A5%8D%E0%A4%A8%E0%A4%BE
MediaWiki 1.47.0-wmf.13
first-letter
मीडिया
विशेष
वार्तालाप
प्रयोगकर्ता
प्रयोगकर्ता वार्ता
विकिपीडिया
विकिपीडिया वार्ता
चित्र
चित्र वार्ता
मीडियाविकि
मीडियाविकि वार्ता
टेम्पलेट
टेम्पलेट वार्ता
मदद
मदद वार्ता
श्रेणी
श्रेणी वार्ता
TimedText
TimedText talk
Module
Module talk
Event
Event talk
नेवादा
0
2029
803299
773317
2026-07-29T16:06:48Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803299
wikitext
text/x-wiki
'''नेवादा''' [[संयुक्त राज्य अमेरिका]] के एगो [[राज्य (प्रांत)|राज्य]] बा।
ई लेख अमेरिकी राज्य के बारे में बा। अन्य उपयोग खातीर, नेवादा (बहुविकल्पी) देखल जाव।
"सिल्वर स्टेट" ई पुनर्निर्देश करता। अन्य उपयोग के खातीर, सिल्वर स्टेट (बहुविकल्पी) देखल जाव।
[C] एकर सीमा उत्तर-पश्चिम में ओरेगन, उत्तर-पूर्व में इडाहो, पश्चिम में कैलिफ़ोर्निया, दक्षिण-पूर्व में एरिज़ोना औरी पूरब में यूटा से लागेला । नेवादा सातवा स्थान पर बा।
नेवादा अमेरिकी राज्य में 7वां सबसे व्यापक, 32वां सबसे अधिक आबादी वाला और 9वां सबसे कम घनी आबादी वाला राज्य ह। नेवादा के लगभग तीन-चौथाई लोग क्लार्क काउंटी में रहेला, जउना में लास वेगास-पैराडाइज़ महानगरीय क्षेत्र शामिल बा, [5] जेमें राज्य के चार सबसे बड़ सम्मिलित सहर में से तीन गो शामिल बा। [6] नेवादा के राजधानी कार्सन सिटी ह। लास वेगास राज्य के सबसे बड़ सहर ह। नेवादा के अपना इतिहास औरी अर्थव्यवस्था में चांदी के महत्व के कारण आधिकारिक तौर पर "सिल्वर स्टेट" के रूप में जानल जाला।
इ "बैटल बॉर्न स्टेट" के रूप में भी जानल जाला कहेकी एमे गृहयुद्ध के दौरान राज्य का दर्जा हासिल कइल गइल बा ("बैटल बॉर्न" शब्द एमे राज्य ध्वज पर भी दिखाई देवेला); अब्राहम लिंकन के अध्यक्षता के कारण, कॉमस्टॉक लॉड द्वारा उ समय उत्पन्न चांदी के अयस्क में लगभग $400 मिलियन की मौद्रिक सहायता के द्वारा नव सम्मानित राज्य के समर्थन से संघ के ज्यादा लाभ भइल रहे। [उद्धरण वांछित] एके इहे नाम के मूल पौधा के खातिर "सेजब्रश स्टेट" के रूप में भी जानल जाला ; औरी "ऋषि-मुर्गी राज्य" के रूप में।[7] स्पैनिश में नाम के अर्थ "बर्फीला" होला, जावन सिएरा नेवादा पर्वत श्रृंखला के साथे नेवादा के छोट ओवरलैप के जिक्र करेला ; हालाँकि, नेवादा के शेष भाग बड़ पैमाना पे रेगिस्तानी और अर्ध-शुष्क बा, एके अधिकतर भाग ग्रेट बेसिन के भीतर बा। ग्रेट बेसिन के दक्खिन के क्षेत्र मोजावे रेगिस्तान के भीतर बा, जबकि ताहो झील और सिएरा नेवादा पश्चिमी किनारे पर स्थित बा। राज्य की लगभग 86% भूमि के प्रबंधन अमिरिकी संघीय सरकार के विभिन्न न्यायालय के द्वारा कइल जाला , नागरिक और सैन्य दुनु। [8]
पाइयूट, शोशोन औरी वाशू जनजाति के अमिरिकी भारतीय अब नेवादा में निवास करेला। इ क्षेत्र का पता लगावे वाला पहले यूरोपीय स्पेनिश रहे।
उ लोग इ क्षेत्र के नेवादा (बर्फीला) कहेला काहेकी जाड़ा के दिन में पहाड़ पे बरफ जम जात रहे, जौन स्पेन के सिएरा नेवादा के जईसन रहे। इ क्षेत्र न्यू स्पेन के वायसरायल्टी के भीतर अल्टा कैलिफ़ोर्निया के क्षेत्र के हिस्सा बनल, जेमे 1821 में मेक्सिको के रूप में स्वतंत्रता प्राप्त भइल। मैक्सिकन-अमेरिकी युद्ध में जीत के बाद संयुक्त राज्य अमेरिका 1848 में इस क्षेत्र पर कब्ज़ा कर लिहल औरी 1850 में इ यूटा क्षेत्र के हिस्सा के रूप में सामिल कर लिहल गइल। 1859 में कॉम्स्टॉक लॉड में चांदी की खोज के कारण जनसंख्या में उछाल आइल जौन एगो प्रेरणा बन गइल। 1861 में पक्षिमी यूटा क्षेत्र से नेवादा क्षेत्र के निर्माण। नेवादा 31 अक्टूबर, 1864 के दूगो राज्य में से दूसरा के रूप में 36वां राज्य बन गइल। गृहयुद्ध के समय संघ में जोड़ल गइल। (पहिला वेस्ट वर्जीनिया रहे)। [9]
नेवादा आपन स्वतंत्रतावादी कानून के खातिर जानल जाला। 1940 में, 110,000 से बेसी लोगन के आबादी के साथे, नेवादा अब तक का सबसे कम आबादी वाला राज्य रहे, जबकि अगीला सबसे कम आबादी वाला राज्य, व्योमिंग के आधा से भी कम आबादी रहे। [10] हालाँकि, वैध जुए औरी उदार वियाह औरी तलाक कानून नेवादा के 20वीं सदी में एगो परमुख पर्यटन अस्थल में बदल दिहल गइल। 11][12] नेवादा एकमात्र अमेरिकी राज्य बा जहां वेश्यावृत्ति के कानूनी अधिकार मिलल बा, हालांकि एके सबसे अधिक आबादी वाला क्षेत्र - क्लार्क काउंटी (लास वेगास), वाशो काउंटी (रेनो) और कार्सन सिटी (जौन एगो स्वतंत्र सहर के रूप में बा) में इ अवैध बा। काउनो भी काउंटी के सीमा के भीतर नइखे)। पर्यटन उद्योग नेवादा का सबसे बड़ा नियोक्ता बनल बा। [13] खनन अर्थव्यवस्था के एगो महत्वपूर्ण क्षेत्र के रूप में जारी बा: नेवादा दुनिया में सोना के चौथा सबसे बड़ा उत्पादक ह। [14] नेवादा सबसे शुष्क राज्य ह, औरी समय के साथ, औरी जलवायु परिवर्तन से प्रभावित होके, नेवादा में सूखा आवृत्ति और गंभीरता में बढ़ रहल बा, [15] जैना से नेवादा के जल सुरक्षा पर औरी दबाव पड़ रहल बा।
शब्द-साधन
"नेवादा" नाम स्पैनिश विशेषण नेवादा ([neˈβaða]) से आइल बा , जेकर अर्थ हवे "बर्फ से तोपल " या "बर्फीला"।[16] राज्य का नाम नेवादा क्षेत्र से लिहल गइल बा, जेकर नाम सिएरा नेवादा के नाम पर रखल गइल बा।
नेवादावासी दूसरे शब्दांश के उच्चारण "सेब" के "ए" (/nɪˈvædə/) के साथ करेला , जबकि राज्य के बाहर के कुछ लोग एकर उच्चारण "हथेली" के "ए" (/nɪˈvɑːdə/) के साथे करेला। [18] हालांकि बाद वाला उच्चारण के गुणवत्ता, लेकिन लंबाई ना, स्पेनिश उच्चारण के करीब बा (स्पेनिश /ए/ खुला केंद्रीय ह [ए],[19] जबकि अमेरिकी अंग्रेजी /ɑː/ पीछे से भिन्न होला [ɑː] से सेंट्रल [äː]),[20] इ नेवादावासि द्वारा इस्तेमाल कइल जाये वाला उच्चारण ना ह ।राज्य विधानसभा सदस्य हैरी मोर्टेंसन नेवादा के वैकल्पिक उच्चारण के मान्यता देवे खातिर एगो विधेयक के प्रस्ताव रखले,[21] हालांकि इ विधेयक के अधिकांश विधायक लाेगिन के समर्थन नइखे मिलल औरी एकरा कबो वोट नइखे मिलल। नेवादन उच्चारण राज्य विधायिका के द्वारा उपयोग कइल जाए वाला उच्चारण ह। एक समय में, राज्य के आधिकारिक पर्यटन संगठन, ट्रैवेलनेवादा राज्य के नाम के "नेवादा" के रूप में शैलीबद्ध काईल गइल रहे, जेमें स्थानीय रूप से पसंदीदा उच्चारण के इंगित करे वाला ए पर एक संक्षिप्त नाम रहे, [22] जौन 2007 तक लाइसेंस प्लेट डिजाइन के रूप में भी उपलब्ध रहे। [23]
इतिहास
मुख्य लेख: नेवादा के इतिहास
अधिक जानकारी: लास वेगास के इतिहास
स्वदेशी इतिहास
यूरोपीय लोगन के आगमन से पहिले, सबसे शुरुआती निवासी स्वदेशी जनजातियो रहे जेमें गोशूट, दक्षिणी पाइयूट लोग, मोहावे लोग और वाशिसिव (वाशू लोग) शामिल रहे।[24][25] 1861 से पहीले
मुख्य लेख: कैलिफ़ोर्निया § इतिहास, और अल्टा कैलिफ़ोर्निया
अधिक जानकारी: कोर्डोबा के संधि, मैक्सिकन साम्राज्य के स्वतंत्रता के घोषणा, पहीला मैक्सिकन साम्राज्य, मैक्सिको के अनंतिम सरकार, पहीला मैक्सिकन गणराज्य, सेंट्रलिस्ट रिपब्लिक ऑफ मैक्सिको, सिएटे लेयेस, और मैक्सिको और स्पेन के बीच शांति और दोस्ती के निश्चित संधि
1824 में मेक्सिको। अल्टा कैलिफ़ोर्निया में आज के नेवादा शामिल रहे।
फ़्रांसिस्को गार्सेस इ क्षेत्र में आए वाला पहीले यूरोपीय रहे। [26] नेवादा के न्यू स्पेन के उत्तर-पश्चिमी क्षेत्र में स्पेनिश साम्राज्य के एगो हिस्सा के रूप में शामिल कर लिहल गइल रहे। प्रशासनिक रूप से, नेवादा के क्षेत्र न्यू स्पेन के वायसरायल्टी में प्रोविंसियास इंटर्नास के कमांडेंसी जनरल के हिस्सा रहे। 1804 में जब कैलिफ़ोर्निया के विभाजन भइल त नेवादा अल्टा कैलिफ़ोर्निया (ऊपरी कैलिफ़ोर्निया) प्रांत के हिस्सा बन गइल। 1821 में मैक्सिकन स्वतंत्रता संग्राम के जीत के साथे, अल्टा कैलिफोर्निया प्रांत छोट आबादी के साथे मैक्सिको के एगो क्षेत्र (राज्य) बन गइल। जेडेदिया स्मिथ 1827 में लास वेगास घाटी में प्रवेश कइल, और पीटर स्केन ओग्डेन ने 1828 में हम्बोल्ट नदी के यात्रा काईलस। जब मॉर्मन 1847 में डेसेरेट राज्य बनल, त उ ग्रेट बेसिन औरी कोलोराडो जलक्षेत्र के भीतर पूरा नेवादा पर दावा कइलस। उ 1851 में, जौन अब नेवादा बा, मॉर्मन स्टेशन (आधुनिक जेनोआ) में पहली श्वेत बस्ती के स्थापना कइल गइल। जून 1855 में, विलियम ब्रिंगहर्स्ट औरी यूटा के 29 साथी मॉर्मन मिशनरी लास वेगास शहर के उत्तर-पूर्व में एगो साइट पर पहुंचले औरी निर्माण भइल 150 फुट वर्गाकार एडोब किला, घाटी में निर्मित पहिला स्थायी संरचना, जौन 1858-1859 कि सर्दियों तक साल्ट लेक सिटी के नियंत्रण में रही।{{अमेरिका}}
{{यूएस-भू-आधार}}
[[श्रेणी:यूनाइटेड स्टेट्स ऑफ अमेरिका के प्रांत]]
9sa3fcw7l6fvzkwpeixji4uumvcz9j5
चाय
0
5572
803366
782522
2026-07-29T16:24:03Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803366
wikitext
text/x-wiki
[[चित्र:Tea leaves steeping in a zhong čaj 05.jpg|256px|thumb|चाय के पतई पानी में सिझावल जात बा]]
'''चाय''' भाषा '''चाह''' ({{Lang|en|tea}}, टी) एगो महकदार पेय (पियल जाये वाला) चीज ह जे [[चाय के पौधा|चाय के पौधवन]] के हरियर चाहे सुखावल [[पतई|पतइन]], जेकरा चायपत्ती कहल जाला, के ऊपर गरम भा खउलत पानी ढरका के; चाहे पानी में अइसन चायपत्ती के खउला तइयार कइल जाला। [[भारतीय उपमहादीप]] में एकर [[दूध]] के साथे पका के बनावल जाये वाला तरीका बेसी चलन में बाटे। चाय के पौधा (कैमेलिया साइनेंसिस) मूल रूप से दक्खिन-पच्छिमी चीन आ उत्तरी म्यांमार के बीचा के बार्डर के आसपास के इलाका के पौधा मानल जाला।
चाय भी बनेला, लेकिन बहुत कम, कैमेलिया टैलिएंसिस के पत्ता से।
सादा पानी के बाद चाय दुनिया में सभसे ढेर खपत होखे वाला पेय हवे।[[iarchive:empireoftearemar00macf/page/32/mode/2up|[9]]] चाय के कई तरह के होला; कुछ में ठंडा करे वाला, हल्का कड़वा आ कसैला स्वाद होला [https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4462044/ <nowiki>[10]</nowiki>] जबकि कुछ में अइसन प्रोफाइल होला जेह में मीठा, अखरोट वाला, फूलन के भा घास के नोट सामिल होला। चाय के मुख्य रूप से कैफीन के मात्रा के चलते मनुष्य में उत्तेजक प्रभाव होखेला।
चाय पीये के सुरुआती बिस्वास जोग रिकार्ड तीसरी सदी ईसवी के हवे, चीनी चिकित्सक हुआ तुओ द्वारा लिखल गइल मेडिकल टेक्स्ट में।[https://books.google.co.in/books?id=YdpL2YCGLVYC&pg=PA63&redir_esc=y#v=onepage&q&f=false <nowiki>[12]</nowiki>] चीनी तांग राजवंश के दौरान एकरा के मनोरंजन खातिर पेय के रूप में लोकप्रिय बनावल गइल आ एकरे बाद चाय पीये के काम पूरबी एशिया के अउरी देस सभ में भी फइल गइल। पुर्तगाली पुजारी आ व्यापारी लोग 16वीं सदी के दौरान एकरा के यूरोप में ले आइल।[https://books.google.co.in/books?id=YdpL2YCGLVYC&pg=PA63&redir_esc=y#v=onepage&q&f=false <nowiki>[13]</nowiki>] 17वीं सदी के दौरान अंग्रेजन के बीच चाय पीये के फैशन हो गइल, जवन कि ब्रिटिश भारत में बड़ पैमाना प चाय के रोपनी शुरू कइले।
हर्बल टी शब्द से मतलब होला अइसन पेय जे कैमेलिया साइनेंसिस से ना बनल होखे। ई फल, पत्ता भा [[पौधा]] के अउरी हिस्सा सभ के इंफ्यूजन हवें, जइसे कि [[गुलाब]] के कूल्ह, कैमोमाइल भा रूइबो के खड़ा हिस्सा। एकरा के टिसाने भा जड़ी-बूटी के जलसेक कहल जा सकेला ताकि चाय के पौधा से बनल चाय से भ्रम ना होखे।
== व्युत्पत्ति के बारे में बतावल गइल बा ==
चाय खातिर बिबिध शब्द सभ के व्युत्पत्ति चीन से दुनिया भर के देस सभ में चाय पीये के संस्कृति आ ब्यापार के संचरण के इतिहास के देखावे ले।[https://books.google.co.in/books?id=_TR_PQAACAAJ&redir_esc=y <nowiki>[14]</nowiki>] दुनिया भर में चाय खातिर लगभग सभ शब्द तीन गो बिसाल समूह में आवे लें: ते, चा आ चाय, अंग्रेजी में चाय, चा भा चार, आ चाय के रूप में मौजूद। तीनों में से सभसे सुरुआती अंग्रेजी में प्रवेश करे वाला चा बाटे जे 1590 के दशक में पुर्तगाली लोग के माध्यम से आइल, जे लोग मकाओ में ब्यापार कइल आ एह शब्द के कैंटोनीज़ उच्चारण उठा लिहल। तीसरा रूप चाय (मतलब "मसालादार चाय") के उत्पत्ति चा के उत्तरी चीनी उच्चारण से भइल जे जमीनी रास्ता से मध्य [[एशिया]] आ फारस के यात्रा कइलस जहाँ ई फारसी अंत यी उठा लिहलस।
== उत्पत्ति आ इतिहास के बारे में बतावल गइल बा ==
'''वनस्पति के उत्पत्ति के बारे में बतावल गइल बा'''
चाय के [[पौधा]] पूरबी एशिया के मूल निवासी हवें आ चाय के उत्पत्ती के संभावित केंद्र इरावाडी नदी के स्रोत के लगे बा जहाँ से ई पंखा के हिसाब से दक्खिन-पूरबी चीन, भारत-चीन आ असम में फइलल। एह तरीका से चाय के पौधा के प्राकृतिक घर के पच्छिम में बर्मा सीमा के किनारे नागालैंड, मणिपुर आ [[मिज़ोरम|मिजोरम]] के बीच के तुलनात्मक रूप से छोट पंखा के आकार के इलाका के भीतर मानल जाला, चीन से हो के पूरब में चेकियांग प्रांत ले, आ एह लाइन से आमतौर पर दक्खिन ओर पहाड़ी सभ से हो के बर्मा आ थाईलैंड ले वियतनाम ले। ऊपर बतावल गइल पच्छिम-पूरबी अक्ष लगभग 2,400 किमी लंबा बा जे देशांतर 95°-120°E ले फइलल बा। उत्तर-दक्खिन के अक्ष लगभग 1,920 किमी के दूरी पर बाटे, ई बर्मा के उत्तरी हिस्सा से शुरू हो के [[अक्षांश]] 29°N युन्नान, टोंगकिन, थाईलैंड, लाओस से हो के अन्नान ले हो के अन्नान ले पहुँचे ले आ अक्षांश 11°N ले पहुँचे ले।[https://www.etymonline.com/word/tea]
[[चीनी]] (छोट पत्ता वाला) प्रकार के चाय (C. sinensis var. sinensis) के उत्पत्ती दक्खिनी चीन में हो सके ला जे संभवतः जंगली चाय के अज्ञात रिश्तेदार सभ के संकरीकरण के साथ भइल होखे। हालाँकि, चूंकि एह चाय के जंगली आबादी के बारे में कौनों जानकारी नइखे, एह से एकर उत्पत्ती अनुमानित बाटे।[https://books.google.co.in/books?id=_TR_PQAACAAJ&redir_esc=y]
अलग-अलग क्लेड बनावे वाला इनहन के आनुवांशिक अंतर के देखत, चीनी असम प्रकार के चाय (C. sinensis var. assamica) के दू गो अलग-अलग माता-पिता हो सके ला – एगो दक्खिनी युन्नान (Xishuangbanna, Pu'er City) में पावल जाला आ दुसरा पच्छिमी युन्नान (Lincang, बाओशन) के बा)। कई किसिम के दक्खिनी युन्नान असम चाय के निकट संबंधित प्रजाति कैमेलिया टैलेन्सिस के साथ संकरित कइल गइल बा।
दक्खिनी युन्नान असम के चाय के बिपरीत, पच्छिमी युन्नान असम के चाय में भारतीय असम प्रकार के चाय (C. sinensis var. assamica भी) के साथ कई गो आनुवांशिक समानता बा। एह तरीका से पच्छिमी युन्नान असम के चाय आ भारतीय असम के चाय दुनों के उत्पत्ती ओह इलाका में एकही मूल पौधा से भइल होखी जहाँ दक्खिन-पच्छिम चीन, भारत-बर्मा आ तिब्बत मिले ला। हालाँकि, भारतीय असम चाय के पश्चिमी युन्नान असम चाय के साथ कौनों हैप्लोटाइप ना होखे के कारण, भारतीय असम चाय के उत्पत्ति स्वतंत्र पालतू बनावे से भइल होखे के संभावना बा।
कुछ भारतीय [[आसाम|असम]] चाय कैमेलिया प्यूबिकोस्टा प्रजाति के साथ संकरित भइल लउके ला।
12 साल के पीढ़ी मान लिहल जाव त चीनी छोट पत्ता वाला चाय लगभग 22,000 साल पहिले असम चाय से अलग होखे के अनुमान लगावल जाला जबकि चीनी असम चाय आ भारतीय असम चाय 2,800 साल पहिले अलग हो गइल रहे। चीनी छोट पत्ता वाला चाय आ असम चाय के विचलन अंतिम हिमनदी अधिकतम के अनुरूप होई।[https://teaorb.com/en-us/blog/origin-of-tea] {{Webarchive|url=https://web.archive.org/web/20230815111058/https://teaorb.com/en-us/blog/origin-of-tea |date=2023-08-15 }}
'''जल्दी चाय पीये के बा'''
प्राचीन पूर्वी एशिया के लोग सदियन से, शायद सहस्राब्दी तक चाय खात रहे, ओकरा बाद कबो एकरा के पेय पदार्थ के रूप में सेवन कइले रहे। कच्चा पतई के निबड़त रहले, सूप भा साग में डालत रहले, भा सुपारी चबावे के संगे किण्वन क के चबावत रहले।
शांग राजवंश के दौरान युन्नान के इलाका में चाय पीये के सुरुआत भइल होखी जहाँ एकर इस्तेमाल औषधीय काम में होखे। इहो मानल जाला कि सिचुआन में "लोग चाय के पत्ता के सेवन खातिर उबाल के बिना अउरी पत्ता भा जड़ी-बूटी के मिला के गाढ़ तरल पदार्थ में बदले लगलें, एह तरीका से चाय के इस्तेमाल औषधीय गढ़ल के रूप में ना बलुक कड़वा फिर भी उत्तेजक पेय के रूप में कइल गइल।"
चीनी कथा सभ में चाय के आविष्कार के श्रेय 2737 ईसा पूर्व में पौराणिक शेननोंग (मध्य आ उत्तरी चीन में) के बतावल जाला, हालाँकि, सबूत बतावे लें कि चाय पीये के सुरुआत चीन के दक्खिन-पच्छिम (सिचुआन/युन्नान इलाका) से भइल हो सके ला।[20] चाय के सबसे पहिले के लिखल रिकार्ड चीन से आवेला। tú 荼 शब्द शिजिंग आ अउरी प्राचीन ग्रंथ सभ में एक किसिम के "कड़वा सब्जी" (苦菜) के बोध करावे खातिर आवे ला आ संभव बा कि ई कई अलग-अलग पौधा सभ के जिकिर कइल गइल होखे जइसे कि सोव थिसल, चिकोरी भा स्मार्टवीड,[22] साथ ही चाय भी।[23] हुआयांग के [[इतिहास]] में ई दर्ज बा कि सिचुआन में बा लोग झोउ राजा के तू भेंट कइल।[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4878758/]
बाद में किन लोग बा राज्य आ एकरे पड़ोसी शु के जीत लिहल आ 17वीं सदी के बिद्वान गु यानवु के अनुसार जे री झी लू (日知錄) में लिखले रहलें: "किन लोग शु के ले लिहला के बाद ही चाय पीये के तरीका सीखल।" ।"[2] चाय के एगो अउरी संभावित सुरुआती संदर्भ किन राजवंश के जनरल लियू कुन द्वारा लिखल एगो चिट्ठी में मिले ला जे निहोरा कइलें कि कुछ "असली चाय" उनके भेजल जाय।[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5788969/]
चाय के सभसे पहिले के ज्ञात [[भौतिकी|भौतिक]] सबूत[25] 2016 में ज़ियान के हान के सम्राट जिंग के मकबरा में मिलल, ई बतावे ला कि कैमेलिया जीनस के चाय हान राजघराना के सम्राट लोग दूसरी सदी ईसा पूर्व से ही पीयत रहे। हान राजवंश के रचना, "द कॉन्ट्रैक्ट फॉर ए यूथ", वांग बाओ द्वारा 59 ईसा पूर्व में लिखल गइल [27] में चाय उबाल के पहिला ज्ञात संदर्भ दिहल गइल बा। युवक द्वारा कइल जाए वाला काम सभ में से अनुबंध में कहल गइल बा कि "ऊ चाय उबाल के बर्तन भर दीहें" आ "ऊ वुयांग में चाय खरीदीहें"।[https://web.archive.org/web/20020803115304/http://chcp.org/tea.html <nowiki>[2]</nowiki>] चाय के खेती के पहिला रिकार्ड भी एह समय के बतावल जाला, एह दौरान चेंगदू के लगे मेंग पर्वत (蒙山) पर चाय के खेती भइल।
चाय पीये के एगो अउरी सुरुआती बिस्वास जोग रिकार्ड 3वीं सदी ईसवी के हवे, हुआ तुओ के एगो मेडिकल टेक्स्ट में, जेकरा में कहल गइल बा कि "[[कड़ा|कड़वा]] t'u पीये से लगातार बेहतर सोचे के पड़े ला।"[https://books.google.co.in/books?id=AGaTAgAAQBAJ&pg=PA28&redir_esc=y#v=onepage&q&f=false <nowiki>[29]</nowiki>] हालाँकि, तांग राजवंश से पहिले चाय -पीना मुख्य रूप से दक्खिनी चीनी प्रथा रहल जे जियानकांग में केंद्रित रहल।[30] चाय के उत्तरी राजवंश के कुलीन लोग द्वारा तिरस्कार कइल गइल, जे लोग एकरा के दही से नीच बतावे ला।[https://books.google.co.in/books?id=3qmwywEACAAJ&q=slaves%27+drink&pg=PA76&redir_esc=y#v=snippet&q=slaves'%20drink&f=false <nowiki>[31]</nowiki>][https://books.google.co.in/books?id=PASE4LVLzQ0C&q=yogurt&pg=PA22&redir_esc=y#v=snippet&q=yogurt&f=false <nowiki>[32]</nowiki>] तांग राजवंश के दौरान ई बहुत लोकप्रिय भइल, जब ई कोरिया, जापान आ वियतनाम में भी फइलल। चाय आ एकरे तइयारी पर लिखल एगो ग्रंथ द क्लासिक ऑफ टी 8वीं सदी के चीनी लेखक लू यू द्वारा लिखल गइल बा। चीन में चाय पीये पर उनकर बहुत असर पड़ला के बारे में जानल जात रहे।
'''विकास के बारे में बतावल गइल बा'''
सदियन के दौरान चाय के प्रोसेसिंग खातिर कई तरह के तकनीक, आ चाय के कई गो अलग-अलग रूप के बिकास भइल। तांग राजवंश के दौरान चाय के भाप में पकावल जाला, फिर कुचल के केक के रूप में आकार दिहल जाला [https://books.google.co.in/books?id=_TR_PQAACAAJ&redir_esc=y <nowiki>[34]</nowiki>] जबकि सोंग राजवंश में ढीला पत्ता वाला चाय के बिकास भइल आ लोकप्रिय भइल। युआन आ मिंग राजवंश के दौरान, बिना ऑक्सीडाइज चाय के पत्ता के पहिले गरम सूखा पैन में हिलावल जात रहे, फिर रोल क के हवा में सुखावल जात रहे, ई प्रक्रिया ऑक्सीकरण प्रक्रिया के रोक देले जवना से पत्ता गहिरा हो जाईत, जवना से चाय हरियर रहेला।
15वीं सदी में ऊलोंग चाय के बिकास भइल जेह में कड़ाही में गरम करे से पहिले पतई सभ के आंशिक रूप से ऑक्सीडाइज होखे दिहल जाला। हालाँकि, पश्चिमी स्वाद पूरा तरीका से ऑक्सीडाइज कइल करिया चाय के पक्ष में रहल आ पतई सभ के अउरी ऑक्सीडाइज होखे दिहल गइल। पीला चाय मिंग राजवंश के दौरान हरी चाय के उत्पादन में आकस्मिक खोज रहल, जब प्रतीत होखे वाला लापरवाह प्रथा के कारण पत्ता पीला हो गइल, जेकरा से अलग स्वाद मिलल।[https://books.google.co.in/books?id=_TR_PQAACAAJ&redir_esc=y <nowiki>[35]</nowiki>]
'''दुनिया भर में फइल गइल'''
चाय के पहिली बेर चीन में पच्छिमी पुजारी आ व्यापारी लोग के 16वीं सदी के दौरान पेश कइल गइल आ ओह समय एकरा के चा कहल गइल।[13] चाय के सभसे सुरुआती यूरोपीय संदर्भ, जेकरा के चियाई के नाँव से लिखल जाला, वेनिस के जियामबतिस्ता रामुसिओ द्वारा 1545 में लिखल डेले नेविगेशनी ई वियाजी से मिलल।[https://books.google.co.in/books?id=_TR_PQAACAAJ&redir_esc=y <nowiki>[36]</nowiki>]
यूरोपीय राष्ट्र द्वारा चाय के पहिला रिकार्ड खेप 1607 में भइल जब डच ईस्ट इंडिया कंपनी मकाओ से चाय के एगो माल जावा ले गइल, फिर दू साल बाद, डच लोग चाय के पहिला असाइनमेंट खरीदल जवन जापान के हिराडो से रहे यूरोप में भेजल गइल।[https://books.google.co.in/books?id=_TR_PQAACAAJ&redir_esc=y <nowiki>[37]</nowiki>] नीदरलैंड के हेग में चाय एगो फैशनेबल पेय बन गइल आ डच लोग एह पेय के जर्मनी, फ्रांस आ अटलांटिक के ओह पार न्यू एम्स्टर्डम (न्यूयॉर्क) में पेश कइल।[https://books.google.co.in/books?id=_TR_PQAACAAJ&redir_esc=y <nowiki>[38]</nowiki>]
1567 में रूसी लोग चाय के संपर्क में आइल जब कोसाक अटामन पेट्रोव आ यालिशेव चीन के दौरा कइलें।[https://www.apollotea.com/tea-articles/tea-history/15-russian-tea-history <nowiki>[39]</nowiki>] मंगोलियाई खान 1638 में ज़ार माइकल प्रथम के चार पूड (65–70 किलोग्राम) चाय दान कइलें। जेरेमिया कर्टिन के अनुसार, संभवतः 1636 में वासिली स्टारकोव के अल्टिन खान के दूत के रूप में भेजल गइल रहे। उनुका के 250 पाउंड के चाय ज़ार के उपहार में दिहल गइल। स्टारकोव पहिले त मना कर दिहलन, मरल पतई के बोझ के कवनो फायदा ना देखत, बाकिर खान जिद कइलन. एह तरह से रूस में चाय के परिचय भइल. 1679 में रूस फर के बदले चीन से ऊंट के कारवां के माध्यम से नियमित चाय के आपूर्ति के संधि कइलस। आज एकरा के वास्तविक रूप से राष्ट्रीय पेय मानल जाला।
अंग्रेजी में चाय के पहिला रिकार्ड रिचर्ड विकहम के लिखल चिट्ठी से आइल जे जापान में ईस्ट इंडिया कंपनी के एगो ऑफिस चलावत रहलें, मकाओ के एगो व्यापारी के लिख के 1615 में "बेस्ट सॉर्ट ऑफ चाव" के निहोरा कइले रहलें।पीटर मुंडी, एगो यात्री आ व्यापारी रहलें जे 1637 में फुजियान में चाय के सामना कइले रहलें, लिखलें कि "चा – खाली पानी जेह में एक तरह के जड़ी-बूटी के बॉयल कइल होखे"।[https://books.google.co.in/books?id=lXYFBQAAQBAJ&pg=PT25&redir_esc=y#v=onepage&q&f=false <nowiki>[43][44]</nowiki>]
1657 में लंदन के एगो कॉफी हाउस में चाय बिकाइल, सैमुअल पेपिस 1660 में चाय के स्वाद लिहलें आ ब्रागांजा के कैथरीन 1662 में चार्ल्स द्वितीय से बियाह कइला पर चाय पीये के आदत के अंग्रेजी दरबार में ले गइली, हालाँकि, चाय के इस्तेमाल बहुत ढेर ना भइल ब्रिटिश दीप सभ में 18वीं सदी ले आ ओह समय के अंतिम हिस्सा ले महंगा रहल। अंग्रेजी पीये वाला लोग काली चाय में चीनी आ दूध डालल पसंद कइल आ 1720 के दशक में काली चाय ग्रीन टी के लोकप्रियता में पाछा छोड़ दिहलस।[https://www.bbc.co.uk/programmes/p004y24y <nowiki>[45]</nowiki>] 18वीं सदी के दौरान चाय के तस्करी के चलते आम जनता चाय के खरीददारी अउरी चाय के सेवन करे में सक्षम हो गइल। ब्रिटिश सरकार चाय पर टैक्स हटा दिहलस, जेकरा से तस्करी के ब्यापार खतम हो गइल, 1785 ले।[https://web.archive.org/web/20090730000451/http://www.tea.co.uk/page.php?id=98 <nowiki>[46]</nowiki>]
ब्रिटेन आ आयरलैंड में शुरू में चाय के सेवन खास मौका पर, जइसे कि धार्मिक परब, जागरण आ घरेलू काम के जमघट पर लग्जरी आइटम के रूप में कइल जात रहे। यूरोप में चाय के दाम 19वीं सदी के दौरान लगातार गिरल, खासतौर पर भारतीय चाय के भारी मात्रा में आवे लागला के बाद; 19वीं सदी के अंत ले चाय [[समाज]] के हर स्तर खातिर रोजमर्रा के पेय पदार्थ बन गइल रहे।[https://web.archive.org/web/20090730000451/http://www.tea.co.uk/page.php?id=98 <nowiki>[47]</nowiki>]
चाय के लोकप्रियता के ऐतिहासिक घटना में भूमिका रहे – 1773 के चाय अधिनियम बोस्टन चाय पार्टी के भड़का दिहलस जवन अमेरिकी क्रांति में बढ़ल। चाय के व्यापार के वजह से ब्रिटिश व्यापार घाटा के मुद्दा के संबोधित करे के जरूरत के परिणामस्वरूप अफीम युद्ध शुरू भईल| किंग कांगसी सम्राट चीन में बिदेसी उत्पाद सभ के बेचे पर रोक लगा दिहले रहलें आ 1685 में फरमान जारी कइलें कि चीन से खरीदल सगरी सामान सभ के भुगतान चांदी के सिक्का भा बुलियन में होखे के चाहीं।[https://books.google.co.in/books?id=mOu_DQAAQBAJ&q=chinese+european+bullion&pg=PT365&redir_esc=y#v=snippet&q=chinese%20european%20bullion&f=false <nowiki>[48]</nowiki>]
एकरा बाद दोसरा राष्ट्रन के व्यापारी एगो अउरी उत्पाद खोजे के कोशिश कइले, एह मामला में अफीम, जवना के चीन के बेचे के कोशिश कइल कि ऊ चाँदी वापस कमा सके जवना के चाय आ अउरी सामानन के पइसा देबे के पड़ी. एकरे बाद चीन सरकार द्वारा अफीम के ब्यापार में कटौती करे के कोसिस सभ के कारण लड़ाई के सुरुआत भइल।[https://books.google.co.in/books?id=YIyV_5wrplMC&pg=PA26&redir_esc=y <nowiki>[49]</nowiki>]
चीनी छोट पत्ता वाला किसिम के चाय के भारत में 1836 में अंग्रेज लोग द्वारा चाय पर चीनी एकाधिकार के तोड़े के कोसिस में ले आवल गइल।[https://books.google.co.in/books?id=YIyV_5wrplMC&pg=PA26&redir_esc=y <nowiki>[50]</nowiki>] 1841 में आर्चीबाल्ड कैम्पबेल कुमाऊन क्षेत्र से चीनी चाय के बीया ले अइलें आ दार्जिलिंग में चाय रोपनी के प्रयोग कइलें। अलुबारी चाय के बगीचा 1856 में खुलल आ दार्जिलिंग चाय के उत्पादन शुरू भइल।[https://books.google.co.in/books?id=_TR_PQAACAAJ&redir_esc=y <nowiki>[51]</nowiki>]
1848 में रॉबर्ट फॉर्च्यून के ऑनरेबल ईस्ट इंडिया कंपनी चीन के मिशन पर भेजले रहे ताकि चाय के संयंत्र के वापस ग्रेट ब्रिटेन ले आवल जा सके। ऊ आपन सफर बहुत गोपनीयता में शुरू कइलें काहें से कि उनकर मिशन पहिला अफीम युद्ध आ दुसरा अफीम युद्ध के बीच के सुस्ती में भइल।[https://books.google.co.in/books?id=_TR_PQAACAAJ&redir_esc=y [52]] ऊ जवन चीनी चाय के पौधा वापस ले अइले, ओकरा के हिमालय में ले आवल गइल, हालांकि अधिकतर ना बचल। अंग्रेजन के पता चलल रहे कि असम आ भारत के पूर्वोत्तर क्षेत्र में चाय के एगो अलग किसिम के स्थानिक बा जवना के बाद चीनी छोट पत्ता वाला प्रकार के चाय के संगे संकरित कइल गइल।<ref>{{Cite web |date=2015-02-23 |title=TED Case Study: Ceylon Tea |url=http://www1.american.edu/ted/ceylon-tea.htm |access-date=2023-10-04 |website=web.archive.org |archive-date=2015-02-23 |archive-url=https://web.archive.org/web/20150223084443/http://www1.american.edu/ted/ceylon-tea.htm |url-status=dead }}</ref>
चीनी रोपनी आ खेती के तकनीक के इस्तेमाल से ब्रिटिश औपनिवेशिक सरकार असम में जमीन के पेशकश क के चाय उद्योग के स्थापना कइलस जे निर्यात खातिर खेती करे के सहमत होखे।[50] चाय के सेवन मूल रूप से खाली एंग्लो-इंडियन लोग करत रहे; हालाँकि, इंडिया टी बोर्ड के सफल बिज्ञापन अभियान के कारण 1950 के दशक में भारत में ई बहुत लोकप्रिय भइल।[50] अंग्रेज लोग 1867 में श्रीलंका (तब के सीलोन) में चाय उद्योग शुरू कइल।[https://web.archive.org/web/20150223084443/http://www1.american.edu/ted/ceylon-tea.htm <nowiki>[53]</nowiki>]
== रासायनिक संरचना के बारे में बतावल गइल बा ==
शारीरिक रूप से देखल जाए त चाय में घोल अउरी निलंबन दुनो के गुण होखेला। ई चाय के पत्ता से निकालल गइल सगरी पानी में घुलनशील यौगिक सभ के घोल हवे, जइसे कि पॉलीफेनोल आ अमीनो एसिड, बाकी जब सभ अघुलनशील घटक सभ के बिचार कइल जाय तब ई निलंबन हवे, जइसे कि चाय के पत्ता में सेल्यूलोज। [उद्धरण के जरूरत बा] चाय के जलसेक वैश्विक स्तर पर सभसे ढेर खपत होखे वाला पेय पदार्थ सभ में गिनल जाला।[54]
कैफीन चाय के सूखा वजन के लगभग 3% हिस्सा होला, जेकर अनुवाद प्रकार, ब्रांड,[55] आ ब्रूइंग के तरीका के आधार पर प्रति 250-मिलीलीटर (8 1⁄2 US fl oz) कप में 30 से 90 मिलीग्राम के बीच होला।[56] एगो अध्ययन में पावल गइल कि एक ग्राम काली चाय में कैफीन के मात्रा 22 से 28 मिलीग्राम के बीच रहे जबकि एक ग्राम ग्रीन टी में कैफीन के मात्रा 11 से 20 मिलीग्राम के बीच रहे, जवन कि एगो महत्वपूर्ण अंतर के देखावेला।[57]
चाय में थियोब्रोमाइन आ थियोफिलिन के भी कम मात्रा होला जे कैफीन नियर ज़ैन्थिन आ उत्तेजक होलें।[58]
चाय में कसैलापन के कारण पॉलीफेनोल के मौजूदगी बतावल जा सकता। ई चाय के पत्ता में सभसे ढेर पावल जाए वाला यौगिक हवें, इनहन के रचना के 30–40% हिस्सा होलें।[59] चाय में मौजूद पॉलीफेनोल सभ में फ्लेवोनोइड, एपिगैलोकैटेचिन गैलेट (EGCG), आ अउरी कैटेचिन सभ के सामिल कइल जाला।[60][61]
हालाँकि, एह बात पर सुरुआती नैदानिक रिसर्च भइल बा कि हरियर भा करिया चाय से मनुष्य के बिबिध बेमारी सभ से बचाव हो सके ला, अइसन कौनों सबूत नइखे मिलल कि चाय के पॉलीफेनोल सभ के स्वास्थ्य पर कौनों परभाव पड़े ला या बेमारी के जोखिम कम करे ला।[62][63]
== स्वास्थ्य पर असर पड़ेला ==
हालाँकि, कैमेलिया साइनेंसिस के सेवन के पूरा इतिहास में स्वास्थ्य के फायदा मानल गइल बा, अइसन कौनों उच्च गुणवत्ता वाला सबूत नइखे मिलल जे ई बतावे कि चाय के सेवन से संभवतः सतर्कता बढ़ावे के अलावा अउरी कौनों खास फायदा मिले ला, ई परभाव चाय के पत्ता में कैफीन के कारण होला।[64][65] 21वीं सदी के सुरुआत में भइल क्लिनिकल रिसर्च में ई पावल गइल कि अइसन कौनों वैज्ञानिक सबूत नइखे मिलल जेह में ई बतावल जा सके कि चाय के सेवन से कौनों भी बेमारी पर परभाव पड़े ला या स्वास्थ्य में सुधार होला।[64]
काली आ हरियर चाय में कौनों जरूरी पोषक तत्व महत्वपूर्ण मात्रा में ना होला, आहार खनिज मैंगनीज के छोड़ के, 0.5 मिलीग्राम प्रति कप या संदर्भ दैनिक सेवन (RDI) के 26% होला।[66] चाय में कबो-कबो फ्लोराइड भी मौजूद होला; कुछ खास किसिम के "ईंट के चाय", पुरान पत्ता आ तना से बनल, सभसे ढेर मात्रा में होला, ई काफी होला कि अगर ढेर चाय पीयल जाय तब स्वास्थ्य के खतरा पैदा हो सके ला, एकर कारण माटी, अम्लीय माटी आ लंबा समय ले ब्रूइंग में फ्लोराइड के ढेर मात्रा बतावल गइल बा .[67]
== खेती आ कटाई के काम होला ==
कैमेलिया साइनेंसिस एगो सदाबहार पौधा हवे जे मुख्य रूप से उष्णकटिबंधीय आ उपोष्णकटिबंधीय जलवायु में बढ़े ला।[68] कुछ किसिम सभ समुंद्री जलवायु के भी सहन क सके लीं आ इंग्लैंड के कॉर्नवाल,[69] स्कॉटलैंड के पर्थशायर,[70] अमेरिका के वाशिंगटन,[71] आ कनाडा के वैंकूवर दीप ले उत्तर में खेती कइल जाला।[72] दक्खिनी गोलार्ध में चाय के खेती तस्मानिया के होबार्ट[73][74] आ न्यूजीलैंड के वाइकाटो ले दक्खिन में होला।[75]
चाय के पौधा सभ के प्रजनन बीया आ कलम से होला; पौधा के बीया पैदा करे खातिर लगभग 4 से 12 साल के जरूरत होला आ नया पौधा के कटाई खातिर तइयार होखे से लगभग तीन साल के जरूरत होला।[68] जोन 8 के जलवायु भा एकरे से ढेर गरम जलवायु के अलावा, चाय के पौधा सभ में हर साल कम से कम 127 सेमी (50 इंच) बरखा के जरूरत होला आ अम्लीय माटी पसंद करे लें।[76]
कई गो उच्च गुणवत्ता वाला चाय के पौधा सभ के खेती समुंद्र तल से 1,500 मीटर (4,900 फीट) ले ऊँचाई पर होला। हालाँकि, एह ऊँचाई सभ पर पौधा सभ के बढ़ती धीरे-धीरे होला, इनहन के स्वाद बेहतर मिले ला।[77]
दू गो प्रमुख किसिम के इस्तेमाल होला: कैमेलिया साइनेंसिस वर्. sinensis, जेकर इस्तेमाल अधिकतर चीनी, फॉर्मोसन आ जापानी चाय सभ खातिर होला आ सी. sinensis var. assamica, पु-एर्ह आ अधिकतर भारतीय चाय (लेकिन दार्जिलिंग ना) में इस्तेमाल होला। एह वनस्पति किसिम सभ के भीतर कई गो उपजाति आ आधुनिक क्लोनल किसिम सभ के जानकारी बा।
चाय के पौधा सभ के वर्गीकरण खातिर पत्ता के आकार मुख्य पैमाना हवे, तीन गो प्राथमिक वर्गीकरण बाड़ें:[78] असम प्रकार, जेकर बिसेसता सभसे बड़हन पत्ता होखे लीं; चीन प्रकार, एकर बिसेसता सभसे छोट पतई होला; आ कंबोडियन प्रकार के, एकर बिसेसता बा कि पत्ता मध्यवर्ती आकार के होलें।
कंबोडियन प्रकार के चाय (C. assamica subsp. lasiocaly) के मूल रूप से असम के चाय के एगो प्रकार मानल जात रहे। हालाँकि, बाद के आनुवांशिक काम से पता चलल कि ई चीनी छोट पत्ता वाला चाय आ असम टाइप के चाय के बीच के संकर हवे।[79] दार्जिलिंग के चाय भी चीनी छोट पत्ता वाली चाय आ असम प्रकार के बड़ पत्ता वाली चाय के बीच के संकर के रूप में लउके ले।[80]
चाय के पौधा के अगर बिना बिघटन के छोड़ दिहल जाय तब 16 मीटर (52 फीट) तक ले के पेड़ में बढ़ जाई [68] बाकी आमतौर पर खेती कइल पौधा सभ के कमर के ऊँचाई ले छंटाई कइल जाला ताकि उखाड़ में आसानी होखे। साथ ही, छोट पौधा सभ में नया अंकुर ढेर होला जे नया आ कोमल पत्ता देला आ चाय के गुणवत्ता बढ़ावे ला।[81]
परिपक्व पौधा के खाली ऊपरी 2.5-5 सेंटीमीटर (1-2 इंच) के चुनल जाला। एह कली आ पतई सभ के 'फ्लश' कहल जाला।[82] एगो पौधा बढ़े के मौसम में हर 7 से 15 दिन में एगो नया फ्लश उगाई। जवन पत्ता सभ के बिकास धीमा होखे, बेहतर स्वाद वाला चाय पैदा करे के परभाव होला।[68]
कई गो चाय निर्दिष्ट फ्लश से उपलब्ध बाड़ी सऽ; उदाहरण खातिर, दार्जिलिंग के चाय फर्स्ट फ्लश (प्रीमियम दाम पर), सेकेंड फ्लश, मानसून आ शरद ऋतु के रूप में उपलब्ध बा। असम के सेकेंड फ्लश भा "टिप्पी" चाय के फर्स्ट फ्लश से बेहतर मानल जाला, काहें से कि पतई पर सोना के नोक लउके ला।
चाय के पौधा सभ के परेशान करे वाला कीड़ा सभ में मच्छर के कीड़ा, हेलोपेल्टिस जीनस सामिल बाड़ें, ई सच्चा कीड़ा हवें आ कुलिसिडी ('मच्छर') परिवार के डिप्टरस कीड़ा सभ से भ्रमित ना होखे के चाहीं। मच्छर के कीड़ा पौधा के सामग्री के चूसे से, आ पौधा के भीतर अंडा देवे (ओविपोजिशन) दुनों से पतई के नुकसान पहुँचा सके ला। सिंथेटिक कीटनाशक के छिड़काव उचित मानल जा सके ला।[83] अउरी कीड़ा सभ में लेपिडोप्टेरन पत्ता फीडर आ चाय के बिबिध बेमारी सभ के नाँव गिनावल जाला।
== Production ==
== इतिहास ==
सबसे पहिले सन् 1815 में कुछ अंग्रेज़ यात्रि सब के ध्यान असम में उगे वाली चाय की झाड़ियन पर गइल जेसे स्थानीय क़बाइली लोग एक प्रकार के पेय बनाके पीयत रहे लोग। [[भारत]] के गवर्नर जनरल ''लॉर्ड बैंटिक'' 1834में चाय के परंपरा भारत में शुरू करे आ उ के उत्पादन करे के संभावना तलाश करे खातिर एगो समिति के गठन कइलन। इ के बाद 1835 में [[आसाम|असम]] में चाय के बाग़ लगावल गइल ।<ref>{{cite news|publisher=BBC News|title=चाय के खेती कब, कहाँ आ कइसे?| url=http://www.bbc.co.uk/hindi/news/story/2006/04/060430_ask_us_tea.shtml|date=30 अप्रैल 2006}}</ref>
{{clear}}
==संदर्भ==
{{reflist}}
{{पेय-आधार}}
[[श्रेणी:पेय]]
fb0ju0kwptdr19d1tj8i53rf36j7mi3
सांकेतिक भाषा
0
9105
803296
724489
2026-07-29T16:06:36Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803296
wikitext
text/x-wiki
[[चित्र:Sign Language.svg|thumbnail]]
[[File:Preservation of the Sign Language (1913).webm|thumb|thumbtime=5|''सांकेतिक भाषा के संरक्षण (१९१३)'']]
'''संकेत भाषा आ सांकेतिक भाषा''' एगो अइसन भाषा हऽ, जोन अर्थ सूचित करे खातिर श्रवणीय ध्वनि परकार में संप्रेषित करे के बजाय, दृश्य रूप में सांकेतिक परकार (हस्तचालित संप्रेषण, अंग-संकेत) संचारित करेला। जेकरा में वक्ता के विचार के धाराप्रवाह रूप से व्यक्त करे खातिर, हाथ के आकार, विन्यास अउर संचालन, हाथ आ देह औरी चेहरा के हाव-भाव के एक साथ उपयोग कइल जाला।
[[श्रेणी:भाषा]]
{{Lang-stub}}
e0ahrpqx31fkc7v6hv679jf9gkmbeit
नरेंद्र मोदी
0
9279
803347
785088
2026-07-29T16:22:30Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803347
wikitext
text/x-wiki
{{Infobox officeholder
|name = नरेंद्र मोदी
|office = [[भारत के प्रधानमंत्री|भारत के 14<sup>वाँँ</sup> परधानमंत्री]]
|image = Prime Minister Of Bharat Shri Narendra Damodardas Modi.jpg
|alt =
|caption = प्रधानमंत्री नरेंद्र मोदी
|predecessor = [[मनमोहन सिंह]]
|president = [[द्रौपदी मुर्मू]]
|succeeding = [[मनमोहन सिंह]]
|successor =
|signature = Signature of Narendra Modi (Hindi).svg
|party = [[भारतीय जनता दल]]
|governor1 = सुन्दरसिंह भण्डारी<br>कैलाशपति मिश्र<br>बलराम जाखड़<br>नवलकिशोर शर्मा<br>एस॰ सी॰ जमीर<br>कमला बेनीवाल
|office1 = गुजरात के 14वाँँ मुख्यमंत्री
|predecessor1 = केशूभाई पटेल
|successor1 = आनंदीबेन पटेल
|birthname = नरेन्द्र दामोदरदास मोदी
|birth_date = {{birth date and age|1950|9|17}}
|birth_place = [[वड़नगर]], [[गुजरात]], [[भारत]]
|death_date =
|death_place =
|spouse = जसोदाबेन चिमनलाल<ref>http://erms.gujarat.gov.in/ceo-gujarat/FileLoc/LS2014/20/Narendra_Modi019006.PDF</ref>
|alma_mater = [[दिल्ली विश्वविद्यालय]]<br>[[गुजरात विश्वविद्यालय]]
| religion = हिन्दू
|website = [http://www.narendramodi.in निजी वेबसाइट]
|term_start = 26 मई 2014
|term_end =
|term_start1 = 7 अक्टूबर 2001
|term_end1 = 22 मई 2014
}}बाड़ें। मोदी 26 मई 2014 के प्रधानमंत्री पद के पहिली बेर शपथ लिहलें; एकरा बाद से ऊ अबतक ले प्रधानमंत्री बाड़ें, अपना तिसरा कार्यकाल क शपथ 9 जून 2024 के लिहलें। भारत के अबतक भइल प्रधानमंत्री लोग में मोदी आजाद भारत में जनमल पहिला प्रधानमंत्री बाड़ें। एकरा पहिले ऊ [[गुजरात]] के मुख्यमंत्री रहलें आ वर्तमान में [[बनारस]] लोकसभा सीट से सांसद बाङे। '''नरेंद्र मोदी''' [[भारतीय जनता दल]] के सदस्य, [[हिंदुत्व|हिंदू राष्ट्रवादी]] आ दक्खिनपंथी संगठन राष्ट्रीय स्वयंसेवक संघ के सदस्य हवें।
बडनगर में एगो गुजराती परिवार में पैदा मोदी, बचपन में अपना बाबूजी के चाय के दुकान पर मदद करें, बाद में आपन दुकान लगावे लगलें। स्वयंसेवक संघ से उनके परिचय आ जुड़ाव आठ बरिस के उमिर में भइल। ग्रेजुएशन के पढ़ाई के बाद मोदी घर छोड़ दिहलें, कारण बियाह बतावल जाला जेकरा के मोदी स्वीकार ना कइलें। दू साल खातिर भारत के बिबिध इलाका सभ में घूमे के बाद 1969 या 1970 में अहमदाबाद लवटलें। 1971 में आरएसएस के फुल-टाइम मेंबर बन गइलें आ 1975 के इमरजेंसी के दौर में कुछ दिन इनके लुकाये के परल। 1985 में आरएसएस इनके भाजपा में भेज दिहलस, पार्टी के बिबिध पद पर रह के काम करत साल 2001 में एकर जनरल सेक्रेटरी बनलें।
2001 में नरेंद्र मोदी के गुजरात के मुख्यमंत्री नियुक्त कइल गइल जवना के कारण तत्काल के मुख्यमंत्री केशूभाई पटेल के खराब सेहत आ भुज के भूडोल के बाद उनके छवि में गिरावट रहल। जल्दिये मोदी बिधायक के रूप में चुन लिहल गइलें। मोदी के प्रशासन के 2002 के गुजरात दंगा में भूमिका होखे के बात कहल जाला,{{efn|2002 के गुजरात दंगा में तत्कालीन प्रशासन के भूमिका खातिर स्रोत संदर्भ<ref name="Buncombe" />}} या फिर दंगा करावे के आरोप भी लगावल जाला, हालाँकि कोर्ट के कौनों अइसन सबूत ना मिलल जेकरा आधार पर एक मामिला में मोदी के कौनों सजा दिहल जा सके।{{efn|2012 में न्यायालय कहलस कि तहकीकात में मोदी के खिलाफ कवनो सबूत ना मिलल।<ref>{{cite web|url=http://www.bbc.com/news/world-asia-india-17664751|title=India Gujarat Chief Minister Modi cleared in riots case|publisher=BBC|date=10 April 2012|accessdate=17 February 2017}}</ref><ref>{{cite web|url=http://www.thehindu.com/news/national/sit-finds-no-proof-against-modi-says-court/article3300175.ece|title=SIT finds no proof against Modi, says court|publisher=दि हिंदू|date=10 April 2012|accessdate=17 February 2017}}</ref>}} मुख्यमंत्री के रूप में गुजरात में आर्थिक बढ़ती के आगे रखे में उनके नीति के तारीफ कइल जाला,<ref>{{Cite news|url=http://www.nytimes.com/2012/02/16/world/asia/16iht-letter16.html|title=Shaking Off the Horror of the Past in India|last=Joseph|first=Manu|date=15 February 2012|work=The New York Times|access-date=19 May 2017|issn=0362-4331}}</ref> जबकि सेहत-सुबिधा, आ शिक्षा के बिकास के आगे बढ़ावे के मामिला में आ गरीबी दूर करे में बिफल रहे खातिर उनके सरकार के आलोचना होले।{{efn|मानवीय बिकास में बिफल रहे के बात कहे वाला स्रोत-संदर्भ।<ref name="Buncombe" />}}
मोदी 2014 के आम चुनाव में भाजपा के अगुआई कइलेन आ पार्टी लोकसभा में बहुमत से आइल। कौनों पार्टी के 1984 के बाद से पूर्ण बहुमत पहिली बेर मिलल बा। मोदी खुद बनारस लोक सभा सीट से चुनल गइलें। परधानमंत्री बनले के बाद से मोदी प्रशासन के कोसिस रहल बा कि देस में बिदेसी डाइरेक्ट निवेश के बढ़ावा दिहल जाय, मूलभूत संरचना पर खरच ढेर कइल जाय, आ स्वास्थ्य आ सामाजिक कल्याण के ऊपर होखे वाला खर्चा में कटौती कइल जाय। मोदी के सारकार द्वारा ब्यूरोक्रेसी के दक्षता बढ़ावे आ आ सेंट्रलाइज हो चुकल पावर के छितरावे के कोसिस में योजना आयोग के भंग क दिहल गइल बा। इनके द्वारा देस भर में हाई-प्रोफाइल सफाई अभियान चलावल गइल बा आ पर्यावरण आ मजूरी संबंधी कानून के कमजोर भा खतम क दिहल गइल बा। मोदी के देस के राजीनीत के दक्खिनपंथी राजनीति के तरफ ओरमावे खातिर इंजीनियरी करे के क्रेडिट दिहल जाला आ इनके हिंदू राष्ट्रवादी बिसवास आ गुजरात दंगा में भूमिका के ले के देस में आ देस के बाहर बिबादास्पद छवी बा; एह दुनों बात के, सामाजिक भेदभाव वाला एजेंडा रखे वाला ब्यक्ति होखे के सबूत के तौर पर भी प्रस्तुत कइल जाला।{{efn|बिबादास्पद भूमिका के चर्चा संबंधी स्रोत-संदर्भ<ref name="Buncombe">{{cite news |title=A rebirth dogged by controversy |first=Andrew|last=Buncombe |url=http://www.independent.co.uk/news/world/asia/a-rebirth-dogged-by-controversy-2357157.html |work=The Independent |date=19 September 2011 |accessdate=10 अक्टूबर 2012 |location=London}}</ref><ref>{{cite book |last=Menon |first=Kalyani Devaki|title=Everyday Nationalism: Women of the Hindu Right in India |year=2012 |publisher=The University of Pennsylvania Press |isbn=978-0-8122-2234-0 |page=26 |quote=Yet, months after this violent pogrom against Muslims, the Hindu nationalist chief minister of Gujarat, Narendra Modi, went to the polls and won a resounding victory}}</ref><ref>{{cite book|last=Mishra |first=Pankaj |title=Perspectives on Modern South Asia: A Reader in Culture, History, and Representation|date=अप्रैल 2011|publisher=Wiley-Blackwell |isbn=978-1-4051-0062-5|page=188|editor-first=Kamala |editor-last=Visweswaran|quote=The chief minister of Gujarat, a young up-and-coming leader of the Hindu nationalists called Narendra Modi, quoted Isaac Newton to explain the killings of Muslims. "Every action", he said, "has an equal and opposite reaction."}}</ref><ref name="CBC">{{cite news|title=Indian PM Narendra Modi still mired in controversy, says expert|publisher=कनाडियन ब्रॉडकास्टिंग कॉर्पोरेशन|date=16 April 2015|accessdate=17 February 2017|url=http://www.cbc.ca/news/canada/british-columbia/indian-pm-narendra-modi-still-mired-in-controversy-says-expert-1.3036836}}</ref><ref>{{cite news |title=Edgy Indian state election going down to the wire |first=Rupam Jain|last=Nair |url=https://www.reuters.com/article/2007/12/12/us-india-state-poll-idUSDEL17441120071212 |agency=[[Reuters]] |date=12 December 2007 |accessdate=10 अक्टूबर 2012}}</ref><ref name="Robinson">{{cite news |title=India's Voters Torn Over Politician |first=Simon |last=Robinson |url=http://content.time.com/time/world/article/0,8599,1693370,00.html |journal=[[टाइम पत्रिका]] |date=11 December 2007 |accessdate=10 अक्टूबर 2012 |archive-date=23 October 2013 |archive-url=https://web.archive.org/web/20131023030008/http://content.time.com/time/world/article/0,8599,1693370,00.html |url-status=dead }}</ref><ref name="Burke">{{cite news |title=Gujarat leader Narendra Modi grilled for 10 hours at massacre inquiry |first1=Jason|last1=Burke |authorlink=:en:Jason Burke |url=https://www.theguardian.com/world/2010/mar/28/gujarat-narendra-modi-massacre-inquiry-india |newspaper=[[दि गार्जियन]] |date=28 मार्च 2010 |accessdate=10 अक्टूबर 2012}}</ref>}}
एकरा बाद वर्ष 2019 में भारतीय जनता दल उनका अगुवाई में दोबारा चुनाव लड़ल औरी एह बेरी पहीले से भी ज्यादा बड़का जीत हासिल भइल। पार्टी कुल 303 सीट पर जीत हासिल कइलस। भाजपा के समर्थक दल यानी राजग के कुल 352 सीट प्राप्त भइल।<ref>{{Cite web |date=2020-11-11 |title=Alliance Wise Election Live Results 2019: Lok Sabha Elections Result Live Alliance Wise, Party Wise |url=https://www.news18.com/lok-sabha-elections-2019/alliance-wise-tally-live-results/ |url-status=dead |archive-url=https://web.archive.org/web/20201111232305/https://www.news18.com/lok-sabha-elections-2019/alliance-wise-tally-live-results/ |archive-date=2020-11-11 |access-date=2024-12-11 |website=web.archive.org}}</ref> 30 मई 2019 के शपथ ग्रहण क के नरेन्द्र मोदी लगातार दूसरी बार प्रधानमन्त्री बनले।<ref>{{Cite web |date=2020-12-18 |title=PM Narendra Modi Becomes Longest Serving Non-Congress Prime Minister |url=https://www.ndtv.com/people/pm-narendra-modi-becomes-longest-serving-non-congress-prime-minister-2279112 |url-status=dead |archive-url=https://web.archive.org/web/20201218122430/https://www.ndtv.com/people/pm-narendra-modi-becomes-longest-serving-non-congress-prime-minister-2279112 |archive-date=2020-12-18 |access-date=2024-12-12 |website=web.archive.org}}</ref>
2019 के आम चुनाव में उनकर पार्टी के जीत के बाद, उनके प्रशासन जम्मू औरी कश्मीर के विशेष राज्य के दर्जा के रद्द कर दिहाल। उनके प्रशासन नागरिकता (संशोधन) अधिनियम, २०१९ भी पेश कइल, जिके परिणामस्वरूप देश भर में व्यापक विरोध प्रदर्शन भइल।
मोदी अपना हिन्दू राष्ट्रवादी विश्वास औरी 2002 के गुजरात दंगा के दौरान उनकर कथित भूमिका पर्वघरेलू औरी अन्तरराष्ट्रीय स्तर पर विवाद के एगो आँकड़ा बनल बाटे,<ref>{{Cite journal |last=Jaffrelot |first=Christophe |date=2013-06-01 |title=Gujarat Elections: The Sub-Text of Modi’s ‘Hattrick’—High Tech Populism and the ‘Neo-middle Class’ |url=https://journals.sagepub.com/doi/10.1177/2321023013482789 |journal=Studies in Indian Politics |language=en |volume=1 |issue=1 |pages=79–95 |doi=10.1177/2321023013482789 |issn=2321-0230}}</ref> जेके एगो बहिष्कारवादी सामाजिक एजेण्डे के प्रमाण के रूप में उद्धृत कइल गइल बा। मोदी के कार्यकाल में, भारत लोकतान्त्रिक बैकस्लेडिंग के अनुभव कइले बा।<ref>{{Cite web |date=2021-08-03 |title=Modi's India {{!}} Princeton University Press |url=https://press.princeton.edu/books/hardcover/9780691206806/modis-india |access-date=2024-12-14 |website=press.princeton.edu |language=en}}</ref><ref>{{Cite web |date=2021-02-27 |title=Democratic Backsliding in India, the World’s Largest Democracy {{!}} V-Dem |url=https://www.v-dem.net/en/news/democratic-backsliding-india-worlds-largest-democracy/ |access-date=2024-12-14 |website=web.archive.org |archive-date=2021-02-27 |archive-url=https://web.archive.org/web/20210227182459/https://www.v-dem.net/en/news/democratic-backsliding-india-worlds-largest-democracy/ |url-status=dead }}</ref>
एकरा बाद वर्ष 2024 में भारतीय जनता दल उनका नेतृत्त्व में दोबारा चुनाव लड़ल औरी एह बार पार्टी कुल 240 सीट पर जीत हासिल कइल। भाजपा के समर्थक दल यानी राजग के कुल 292 सीट प्राप्त भइल।<ref>{{Cite web |date=2020-11-11 |title=Alliance Wise Election Live Results 2019: Lok Sabha Elections Result Live Alliance Wise, Party Wise |url=https://www.news18.com/lok-sabha-elections-2019/alliance-wise-tally-live-results/ |url-status=dead |archive-url=https://web.archive.org/web/20201111232305/https://www.news18.com/lok-sabha-elections-2019/alliance-wise-tally-live-results/ |archive-date=2020-11-11 |access-date=2024-12-16 |website=web.archive.org}}</ref> 9 जून 2024 के शपथ ग्रहण क के नरेन्द्र मोदी लगातार तीसरा बार प्रधानमन्त्री बनले। शपथ ग्रहण के बाद उ पहला आइसन अन कांग्रेसी प्रधानमंत्री बनले जे 1962 के बाद लगातार तीसरा बार प्रधानमंत्री बनले औरी उ पंडित जवाहर लाल नेहरू के बराबरी कइले।<ref>{{Cite web |date=2020-12-18 |title=PM Narendra Modi Becomes Longest Serving Non-Congress Prime Minister |url=https://www.ndtv.com/people/pm-narendra-modi-becomes-longest-serving-non-congress-prime-minister-2279112 |url-status=live |archive-url=https://web.archive.org/web/20201218122430/https://www.ndtv.com/people/pm-narendra-modi-becomes-longest-serving-non-congress-prime-minister-2279112 |archive-date=2020-12-18 |access-date=2024-12-17 |website=web.archive.org}}</ref>
मोदी आपन अगीला दु साल उत्तर औरी उत्तर-पूरब भारत के यात्रा में बितवले।<ref>{{Cite journal |date=2024-12-28 |title=Narendra Modi |url=https://en.m.wikipedia.org/wiki/Narendra_Modi#CITEREFMarino2014 |journal=Wikipedia |language=en}}</ref> साक्षात्कार में, उ स्वामी विवेकानन्द द्वारा स्थापित हिंदू आश्रम के दौरा करे के वर्णन बा: कलकता के पास बेलूर मठ, अल्मोडा में अद्वैत आश्रम औरी राजकोट में रामकृष्ण मिशन। प्रत्येक आश्रम में उनके प्रवास संक्षिप्त रहे काहेकी उनके पास आवश्यक कॉलेज शिक्षा के अभाव रहे। <ref>{{Cite web |title=Business News Today: Read Latest Business news, India Business News Live, Share Market & Economy News |url=https://economictimes.indiatimes.com/ |access-date=2024-12-28 |website=The Economic Times |language=en}}</ref> मोदी के जीवन में विवेकानन्द का बड़ा प्रभाव रहल बा।<ref>{{Cite book|url=https://doi.org/10.4324/9781315696584|title=Swami Vivekananda|date=2015-06-12|publisher=Routledge India|isbn=978-1-317-44637-8|editor-last=Paranjape|editor-first=Makarand R.}}</ref> 1968 के मध्य में, मोदी बेलूर मठ पहुचलन लेकिन उनकारा के लौटा दिहल गइल , जेकरा बाद उ सिलीगुड़ी औरी गुवाहाटी में रुकत कलकत्ता, पश्चिम बंगाल औरी असम के दौरा किइलेन।
एकरा बाद उ 1968 से 1969 तक दिल्ली औरी राजस्थान के रास्ता गुजरात लौटे से पहले, अल्मोडा में रामकृष्ण आश्रम गइलन, जहां उनके फेर से अस्वीकार कर दिहाल गइल। 1969 के अंत में चाहे 1970 के शुरुआत में, उ अहमदाबाद के खातिर फेर से रवाना होखे से पहीले एगो संक्षिप्त यात्रा के खातिर वडनगर लवातले। ,<ref>{{Cite journal |date=2024-12-30 |title=Narendra Modi |url=https://en.m.wikipedia.org/wiki/Narendra_Modi#CITEREFMarino2014 |journal=Wikipedia |language=en}}</ref><ref>{{Cite journal |date=2024-12-30 |title=Narendra Modi |url=https://en.m.wikipedia.org/wiki/Narendra_Modi#CITEREFPanda2016 |journal=Wikipedia |language=en}}</ref> जहां उ अपना चाचा के साथे रहत रहले औरी गुजरात राज्य सड़क परिवहन निगम में अपना चाचा के कैंटीन में काम करत रहले।<ref>{{Cite journal |date=2024-12-30 |title=Narendra Modi |url=https://en.m.wikipedia.org/wiki/Narendra_Modi#CITEREFMukhopadhyay2013 |journal=Wikipedia |language=en}}</ref>
अहमदाबाद में, मोदी इनामदारी के साथे अपना परिचय के नवीनीकृत कइलान, जौन शहर में हेडगेवार भवन (आरएसएस मुख्यालय) में स्थित रहे। <ref>{{Cite web |date=1999-11-30 |title=The man behind Modi: Lakshmanrao Inamdar |url=https://www.indiatoday.in/magazine/the-big-story/story/20140519-narendra-modi-lakshmanrao-inamdar-rss-802639-1999-11-29 |access-date=2025-01-02 |website=India Today |language=en}}</ref><ref>{{Cite news |last=Mukhopadhyay |first=Nilanjan |date=2014-05-14 |title=Narendra Modi: The making of the political leader |url=http://www.livemint.com/Politics/9NIOOTsZCFWnuFX5OO9XNL/Narendra-Modi-The-making-of-the-political-leader.html |access-date=2025-01-02 |work=mint |language=en}}</ref><ref>{{Cite journal |date=2025-01-01 |title=Narendra Modi |url=https://en.m.wikipedia.org/wiki/Narendra_Modi#CITEREFMarino2014 |journal=Wikipedia |language=en}}</ref> एगो वयस्क के रूप में मोदी के पहला ज्ञात राजनीतिक गतिविधि 1971 में रहे जब उ बांग्लादेश मुक्ति संग्राम में लडे खातिर अटल बिहारी वाजपेयी के नेतृत्व में दिल्ली में जनसंघ के सत्याग्रह में शामिल भईल रहले<ref>{{Cite web |title=A Sataygrah and Asatyagraha: Narendra Modi and the Liberation of Bangladesh |url=https://thewire.in/history/a-sataygrah-and-asatyagraha-narendra-modi-and-the-liberation-of-bangladesh |access-date=2025-01-02 |website=The Wire |language=en}}</ref><ref>{{Cite web |last=Lahiri |first=Ishadrita |date=2021-03-27 |title=Was Modi Arrested for Bangladesh Satyagraha? Here’s What We Know |url=https://www.thequint.com/news/politics/was-modi-arrested-for-bangladesh-satyagraha-in-1971-here-is-what-we-know#read-more |access-date=2025-01-02 |website=TheQuint |language=en}}</ref>
इंदिरा गांधी के नेतृत्व वाला केंद्र सरकार मुक्ति वाहिनी के खुला समर्थन पे रोक लगा दिहाल; मोदी के अनुसार, उनके कुछ समय खातिर तिहाड़ जेल में रखल गइल रहे। <ref>{{Cite web |date=2021-03-27 |title=Delhi confidential: The Satyagraha |url=https://indianexpress.com/article/delhi-confidential/narendra-modi-satyagraha-bangladesh-liberation-fighters-delhi-confidential-7247247/ |access-date=2025-01-03 |website=The Indian Express |language=en}}</ref><ref>{{Cite web |title=Political slugfest over PM Modi’s ‘Satyagraha for Bangladesh’ remarks |url=https://www.tribuneindia.com/news/nation/political-slugfest-over-pm-modis-satyagraha-for-bangladesh-remarks-230844 |access-date=2025-01-03 |website=The Tribune |language=en}}</ref><ref>{{Cite news |date=2021-03-27 |title=Modi's satyagraha talk in Dhaka sparks online war |url=https://timesofindia.indiatimes.com/india/modis-satyagraha-talk-in-dhaka-sparks-online-war/articleshow/81717548.cms |access-date=2025-01-03 |work=The Times of India |issn=0971-8257}}</ref> 1971 के भारत-पाकिस्तान युद्ध के बाद, मोदी अपना चाचा के नौकरी छोड़ दिहाले और आरएसएस के पूर्णकालिक प्रचारक बन गइले,<ref name="Narendra Modi">{{Cite journal |date=2025-01-03 |title=Narendra Modi |url=https://en.m.wikipedia.org/wiki/Narendra_Modi#CITEREFMukhopadhyay2013 |journal=Wikipedia |language=en}}</ref> इनामदार के अधीन काम करते हुए। <ref name="Narendra Modi"/>
लड़ाई से कुछ समय पहीले, मोदी भारत सरकार के खिलाफ नई दिल्ली में एगो अहिंसक विरोध प्रदर्शन में भाग लिहाले, जेकरा खातिर उनका के गिरफ्तार कर लिहाल गइल; इ गिरफ्तारी के कारण, इनामदार मोदी के मार्गदर्शन करे के निर्णय लिहाल।<ref>{{Cite journal |date=2025-01-04 |title=Narendra Modi |url=https://en.m.wikipedia.org/wiki/Narendra_Modi#CITEREFMarino2014 |journal=Wikipedia |language=en}}</ref> मोदी के अनुसार, उ एगो आइसन सत्याग्रह के हिस्सा रहले जौना के कारण राजनीतिक लड़ाई भइल।<ref>{{Cite web |title=Political slugfest over PM Modi’s ‘Satyagraha for Bangladesh’ remarks |url=https://www.tribuneindia.com/news/nation/political-slugfest-over-pm-modis-satyagraha-for-bangladesh-remarks-230844 |access-date=2025-01-05 |website=The Tribune |language=en}}</ref><ref>{{Cite journal |date=2024-11-30 |title=Prime Minister's Office (India) |url=https://en.m.wikipedia.org/wiki/Prime_Minister's_Office_(India) |journal=Wikipedia |language=en}}</ref>
1978 में, मोदी दिल्ली विश्वविद्यालय के स्कूल ऑफ ओपन लर्निंग<ref>{{Cite web |date=2017-03-14 |title=Narendra Modi degree row: DU college says it has no data of students passing out in 1978 |url=https://www.indiatoday.in/india/story/narendra-modi-degree-controversy-delhi-university-rti-965536-2017-03-14 |access-date=2025-01-05 |website=India Today |language=en}}</ref> से राजनीति विज्ञान में कला स्नातक (बीए) के डिग्री प्राप्त काइले। <ref>{{Cite news |date=2016-05-10 |title=Narendra Modi’s degree ‘authentic’: Delhi University Registrar Tarun Das |url=https://www.thehindu.com/news/cities/Delhi/Narendra-Modi%E2%80%99s-degree-%E2%80%98authentic%E2%80%99-Delhi-University-Registrar-Tarun-Das/article60504479.ece |access-date=2025-01-07 |work=The Hindu |language=en-IN |issn=0971-751X}}</ref><ref>{{Cite web |date=2013-09-13 |title=Narendra Modi: From tea vendor to PM candidate |url=https://www.indiatoday.in/india/north/story/narendra-modi-from-tea-vendor-to-pm-candidate-210939-2013-09-12 |access-date=2025-01-07 |website=India Today |language=en}}</ref> 1983 में, उ गुजरात विश्वविद्यालय से राजनीति विज्ञान में मास्टर ऑफ आर्ट्स (एमए) के डिग्री प्राप्त कइलन, और बाहरी दूरस्थ शिक्षा के छात्र के रूप में प्रथम श्रेणी में स्नातक के उपाधि प्राप्त कइलान <ref>{{Cite web |date=2014-08-19 |title=Modi proves to be an astute strategist - Hindustan Times |url=http://www.hindustantimes.com/india-news/modi-proves-to-be-an-astute-strategist/article1-264941.aspx |access-date=2025-01-07 |website=web.archive.org |archive-date=2014-08-19 |archive-url=https://web.archive.org/web/20140819083702/http://www.hindustantimes.com/india-news/modi-proves-to-be-an-astute-strategist/article1-264941.aspx |url-status=dead }}</ref><ref>{{Cite news |last=Avasthi |first=Yogesh |date=2016-05-10 |title=Globetrotting PM Modi was weakest in 'International Relations' |url=https://economictimes.indiatimes.com/news/politics-and-nation/globetrotting-pm-modi-was-weakest-in-international-relations/articleshow/52200607.cms |access-date=2025-01-07 |work=The Economic Times |issn=0013-0389}}</ref>।<ref>{{Cite web |date=2016-05-02 |title=Degree row- PM Modi MA with first class: Gujarat University |url=https://indianexpress.com/article/india/india-news-india/pm-modi-scored-62-3-in-ma-from-gujarat-univerisity-vc-2779138/ |access-date=2025-01-07 |website=The Indian Express |language=en}}</ref> उनके बीए और एमए डिग्रि के प्रामाणिकता के लेके विवाद बा।<ref>{{Cite web |last=Team |first=N. L. |date=2020-10-03 |title=The curious case of TOI's report on Modi's 'dear friend' |url=https://www.newslaundry.com/2020/10/03/the-curious-case-of-tois-report-on-modis-dear-friend |access-date=2025-01-07 |website=Newslaundry |language=en}}</ref><ref>{{Cite web |last=Bhattacharya |first=Tathagata |date=2018-06-16 |title=The curious case of Prime Minister Narendra Modi’s degree |url=https://www.nationalheraldindia.com/india/the-curious-case-of-prime-minister-narendra-modis-degree |access-date=2025-01-07 |website=National Herald |language=en}}</ref>
<ref>{{Cite journal |date=2024-11-27 |title=The Caravan |url=https://en.m.wikipedia.org/wiki/The_Caravan |journal=Wikipedia |language=en}}</ref> द इकोनॉमिक टाइम्स के अनुसार, मोदी के इरादा अपना पाठक लोगन के आरएसएस की कार्यप्रणाली समझावेके रहे, औरी आरएसएस के सदस्य के आश्वस्त करे के रहे कि उ वैचारिक रूप से उनके साथे जुड़ल रहियन। प्रधान मंत्री बनले के बाद उ एग्जाम वॉरियर्स नाम के एगो पुस्तक भी लिखले, जौन बच्चा सब के खातिर सराहनीय परीक्षा के सामना करे खातिर एगो मार्गदर्शिका रहे। मोदी आठ गो अन्य किताब भी लिखलन , जेमें ज्यादातर बच्चा सब के खातिर लघु कहानि बा।
<ref>{{Cite news |date=2008-04-09 |title=Jyotipunj: Narendra Modi writes on 'my organisation, my leaders' |url=https://economictimes.indiatimes.com/news/politics-and-nation/jyotipunj-narendra-modi-writes-on-my-organisation-my-leaders/articleshow/2936428.cms |access-date=2025-01-19 |work=The Economic Times |issn=0013-0389}}</ref> मोदी दूंगों गुजराती गरबा गीत के बोल लिखले बाड़े; गरबो, ध्वनि भानुशाली द्वारा गावल गइल औरी तनिष्क बागची द्वारा संगीतबद्ध कइल गइल, औरी माडी, दिव्या कुमार द्वारा गावल गइल और मीत ब्रदर्स द्वारा संगीतबद्ध कइल गइल। गाने 2023 में शरद ऋतु नवरात्रि के अवसर पर जारी कइल गइल रहे। <ref>{{Cite journal |date=2025-01-18 |title=Narendra Modi |url=https://en.m.wikipedia.org/wiki/Narendra_Modi#cite_note-559 |journal=Wikipedia |language=en}}</ref><ref>{{Cite book|url=https://doi.org/10.1093/oso/9780190912468.003.0008|title=Business-Friendly Gujarat Under Narendra Modi|last=Jaffrelot|first=Christophe|date=2019-01-02|publisher=Oxford University Press|isbn=978-0-19-091246-8|pages=211–233}}</ref>
एबंडेंस इन मिलेट्स, फालू औरी गौरव शाह के एगो गाना, जेमे बाजरा के प्रचार खातिर मोदी द्वारा दिहल गइल भाषण रहे, के 2024 ग्रैमी अवार्ड्स खातिर सर्वश्रेष्ठ वैश्विक संगीत प्रदर्शन श्रेणी में नामांकन मिलल।<ref>{{Cite web |title=Song Featuring PM Modi Nominated For Grammy Awards |url=https://www.ndtv.com/india-news/song-featuring-pm-narendra-modi-nominated-for-grammy-awards-4565922 |access-date=2025-01-21 |website=www.ndtv.com |language=en}}</ref><ref>{{Cite web |title=2024 GRAMMYs: See The Full Winners & Nominees List {{!}} GRAMMY.com |url=https://www.grammy.com/news/2024-grammys-nominations-full-winners-nominees-list |access-date=2025-01-21 |website=grammy.com}}</ref>
* 41वां लोकमान्य तिलक राष्ट्रीय पुरस्कार 2023 से प्रधानमंत्री नरेंद्र मोदी के सम्मानित कइल गइल।
* 2014 के लोकसभा चुनाव में भारतीय जनता पार्टी के नेतृत्व वाला राष्ट्रीय जनतांत्रिक गठबंधन (एनडीए) के भारी जीत के बाद, मोदी 26 मई 2014 के भारत के प्रधान मंत्री के रूप में शपथ लीहले, उ देश के आजादी के बाद पैदा होने वाला पहिला भारतीय प्रधान मंत्री बन गइले।
* 2001 में, केशुभाई पटेल के स्वास्थ्य ख़राब चलत रहे और भाजपा उप-चुनावों में कुछ राज्य विधानसभा सीट हार गइल। सत्ता के दुरुपयोग, भ्रष्टाचार और खराब प्रशासन के आरोप लगा वल गइल औरी 2001 में भुज में आइल भूकंप से निपटे खातिर उनके प्रशासन के तरीका से पटेल के प्रतिष्ठा के नुकसान पहुंचल रहे।
== निजी जीवन ==
नरेंद्र मोदी के जाति के पारंपरिक परंपरा के अनुसार, उनकर परिवार जशोदाबेन चिमनलाल मोदी के साथ सगाई कर दिहाल, जेकरा बाद उनकर शादी तब भइल जब उ 17 बरिस के रहली औरी इ 18 बरिस के रहले।<ref name=":0">{{Cite book|url=https://doi.org/10.5149/northcarolina/9781469629537.003.0003|title=The Child Was to Be His Wife|last=Syrett|first=Nicholas L.|date=2016-10-03|publisher=University of North Carolina Press|isbn=978-1-4696-2953-7}}</ref><ref>{{Cite news |date=2018-02-07 |title=Narendra Modi’s Estranged Wife Escapes Unhurt From Car Crash in India (Published 2018) |url=https://www.nytimes.com/2018/02/07/world/asia/india-narendra-modi-wife.html |access-date=2024-12-27 |language=en}}</ref> एके तुरंत बाद, उ अपन पत्नी के छोड़ दिहाले।<ref name=":0" /> और घर छोड़ दिहाले। इ जोडा कबो तलाक ना लिहल लेकिन कई दशक तक उनके सार्वजनिक घोषणा में इ वियाह के जिक्र ना रहे।<ref>{{Cite news |date=2018-02-07 |title=Narendra Modi’s Estranged Wife Escapes Unhurt From Car Crash in India (Published 2018) |url=https://www.nytimes.com/2018/02/07/world/asia/india-narendra-modi-wife.html |access-date=2024-12-27 |language=en}}</ref> अप्रैल 2014 में, राष्ट्रीय चुनाव से कुछ समय पहीले, जेमें उ सत्ता हासिल कइले, मोदी सार्वजनिक रूप से पुष्टि काइले कि उ शादीशुदा हाउवान और उनकर पत्नी जशोदाबेन हइ।<ref>{{Cite web |date=2018-09-13 |title=Narendra Modi Fast Facts - CNN |url=https://edition.cnn.com/2015/01/19/world/narendra-modi-fast-facts/index.html |url-status=live |archive-url=https://web.archive.org/web/20180913201412/https://edition.cnn.com/2015/01/19/world/narendra-modi-fast-facts/index.html |archive-date=2018-09-13 |access-date=2024-12-27 |website=सीएनएन}}</ref> मोदी के एगो जीवनी लेखक ने लिखल बा कि मोदी शादी के गुप्त रखल काहेकी उ आरएसएस में प्रचारक ना बन पवते, जेकरा खातिर कबों ब्रह्मचर्य के आवश्यकता हत रहे।<ref>{{Cite web |date=2013-09-13 |title=Narendra Modi: From tea vendor to PM candidate |url=https://www.indiatoday.in/india/north/story/narendra-modi-from-tea-vendor-to-pm-candidate-210939-2013-09-12 |access-date=2024-12-27 |website=इंडिया टुडे |language=en}}</ref>
== गुजरात के मुख्यमंत्री के रूप में ==
2001 में केशुभाई पटेल (तत्कालीन मुख्यमंत्री) के सेहत बिगड़े लगल रहे औरी भाजपा चुनाव में काइगो सीट हारत रहे।<ref>{{Cite book|url=https://books.google.com/books?id=qT7QvviGoJsC&pg=PA116|title=Business Standard Political Profiles of Cabals and Kings|date=2009|publisher=Business Standard Books|isbn=978-81-905735-4-2|language=en}}</ref> एकरा बाद भाजपा के राष्ट्रीय अध्यक्ष मुख्यमंत्री के रूप में मोदी के नया उम्मीदवार के रूप में रखले । हालांकि भाजपा के नेता लालकृष्ण आडवाणी, मोदी के सरकार चलावे के अनुभव के कमी के कारण चिंतित रहले। मोदी पटेल के उप मुख्यमंत्री बनले के प्रस्ताव के ठुकरा दिहाले औरी आडवाणी व अटल बिहारी वाजपेयी से कहके कि यदि गुजरात के जिम्मेदारी देवेके बा त पूरा दीहल जाओ अन्यथा ना दिहाल जाओ। 3 अक्टूबर 2001 के इ केशुभाई पटेल के जगह गुजरात के मुख्यमंत्री बनले। एकरा साथ ही उनका पर दिसम्बर 2002 में होखे वाला चुनाव के पूरा जिम्मेदारी भी रहे।<ref>{{Cite web |title=Narendra Modi – Leading the race to 7 RCR |url=https://zeenews.india.com/news/general-elections-2014/pm-candidates/narendra-modi-leading-the-race-to-7-rcr_921104.html |access-date=2024-06-02 |website=Zee News |language=en}}</ref> सुरुआती जीवन आ शिक्षा ==
नरेंद्र मोदी के जनम 17 सितंबर 1950 के एगो गुजराती परिवार में, मेहसाणा जिला (ओह समय के बांबे स्टेट, अब गुजरात) के वडनगर में भइल। बाबूजी दामोदरदास मूलचंद मोदी (c.1915 - 1989) आ महतारी हीराबेन मोदी (जनम c.1920) के छह गो में नरेंद्र मोदी तिसरा संतान रहलें।{{sfn|Marino|2014|pp=13, 15, 29–30, 74}} मोदी के परिवार मोध-घाँची-तेली समुदाय के हवे,<ref>{{Cite news|url=http://www.dnaindia.com/india/report-narendra-modi-belongs-to-modh-ghanchi-caste-which-was-added-to-obcs-categories-in-1994-says-gujarat-government-1986389|title=Narendra Modi belongs to Modh-Ghanchi caste, which was added to OBCs categories in 1994, says Gujarat government {{!}} Latest News & Updates at Daily News & Analysis|date=9 May 2014|work=dna|access-date=19 May 2017|language=en-US}}</ref><ref>{{Cite news|url=http://indianexpress.com/article/india/india-news-india/pm-modi-brother-asks-teli-community-to-adopt-modi-prefix-4386125/|title=PM Modi's brother asks Teli community to adopt 'Modi' prefix|date=20 November 2016|work=The Indian Express|access-date=19 May 2017|language=en-US}}</ref><ref>{{Cite news|url=http://timesofindia.indiatimes.com/city/ahmedabad/Modi-is-a-Teli-Ghanchi-OBC-BJP/articleshow/34084111.cms|title='Modi is a Teli-Ghanchi OBC': BJP - Times of India|work=दि टाइम्स ऑफ इंडिया|access-date=19 May 2017}}</ref> जेकरा के सरकारी तौर पर अन्य पिछड़ा वर्ग (ओबीसी) में गिनल जाला।<ref name=" TOI Modh-Ghanchi-Teli">{{cite news|url=http://timesofindia.indiatimes.com/city/ahmedabad/Modi-is-a-Teli-Ghanchi-OBC-BJP/articleshow/34084111.cms|title= 'Modi is a Teli-Ghanchi OBC': BJP|work=दि टाइम्स ऑफ इंडिया|date=23 April 2014|accessdate=17 February 2017}}</ref><ref>{{Cite news|url=http://www.business-standard.com/article/opinion/the-office-of-prime-minister-a-largely-north-indian-upper-caste-hindu-affair-114050700846_1.html|title=The office of Prime Minister: A largely north Indian upper-caste, Hindu affair|last=Ghai|first=Rajat|date=7 May 2014|work=Business Standard India|access-date=19 May 2017}}</ref>
लरिकाईं में मोदी वडनगर रेलवे टीशन पर चाय बेचे में अपने बाबूजी के मदद करें, बाद में अपना भाई के साथे खुद के छाया के दुकान बस अड्डा के लगे चलवलें।<ref>{{Cite news|url=http://www.oneindia.com/international/i-have-special-association-with-tea-modi-tells-lankan-tamils-2433622.html|title='I have a special association with tea', Modi tells Lankan Tamils|work=www.oneindia.com|access-date=19 May 2017|language=en}}</ref><ref>{{Cite book|url=https://books.google.co.in/books?id=ALWaDAAAQBAJ&lpg=PA1&pg=PA20#v=onepage&q&f=false|title=High School Essays|last=Sinha|first=N. C.|publisher=|year=|isbn=978-93-5186-604-6|location=|pages=20|language=en}}</ref> मोदी साल 1967 में वडनगर से इंटर के इम्तिहान पास कइलें, जहाँ उनके एगो अध्यापक औसत दर्जा के बिद्यार्थी आ बाद-बिबाद में रूचि रखे वाला आ थियेटर में लगाव रखे वाला बिद्यार्थी के रूप में बतवलें।<ref name="Jose Caravan">{{cite news|url=http://www.caravanmagazine.in/reportage/emperor-uncrowned?page=1,1|title=The Emperor Uncrowned|last=Jose|first=Vinod K.|date=1 मार्च 2012|work=The Caravan|pages=2–4|authorlink=:en:Vinod Jose|accessdate=11 April 2013|archive-date=23 September 2015|archive-url=https://web.archive.org/web/20150923201924/http://www.caravanmagazine.in/reportage/emperor-uncrowned?page=1,1|url-status=dead}}</ref> बाद-बिबाद प्रतियोगिता में मोदी के कुशलता उनके संघे पढ़े वाला बिद्यार्थी आ उनके मास्टर साहब लोग चिन्हित कइल।{{sfn|Marino|2014|p=16}} मोदी नाटकन (थियेटरन) में बहुत बिसाल व्यक्तित्व (लार्जर-देन-लाइफ) वाला किरदार निभावल पसंद करें जे उनके राजनीतिक कैरियर में भी झलके ला।{{sfn|Mukhopadhyay|2013|p=82}}<ref>{{cite news |url=http://www.newkerala.com/news/2014/fullnews-24257.html |title=Modi's life dominates publishing space (Election Special) |work=New Kerala |date=14 मार्च 2014 |accessdate=4 April 2014}}</ref>
आठ बरिस के उमिर में मोदी के राष्ट्रीय स्वयंसेवक संघ (आरएसएस) के बारे में मालुम भइल आ ऊ लोकल शाखा में जाए सुरू कइलेन। एकरे बाद मोदी लक्ष्मणराव इनामदार, जिनके वकील साहब के नाँव से जानल जाय, मिलने आ उहे इनका के बालस्वयंसेवक बनवलें आ इनके राजनीतिक अभिभावक बनलें।{{sfn|Marino|2014 |p=24}} आरएसएस में अपना शिक्षा के दौरान मोदी वसंत गजेंद्रगाडकर आ नाथालाल जघड़ा से मिललें जे लोग भारतीय जनसंघ के नेता आ 1980 में भारतीय जनता दल के गुजरात इकाई के संस्थापक सदस्य रहल लोग। पढ़ाई के दौरान लरिकाईंये में इनके बियाह जसोदाबेन नरेंद्रभाई मोदी से तय हो गइल जेकरा के मोदी ना स्वीकार कइलें {{sfn|Marino|2014|p=21}} आ एही कारन उपजल पारिवारिक तनाव के कारन 1967 में घर छोड़ देवे के फैसला कइलें।{{sfn|Marino|2014|pp=22–23}}
एकरे बाद मोदी दू साल उत्तर भारत आ [[पूर्बोत्तर भारत|पूर्वोत्तर भारत]] के जगह-जगह घुमलें, कवना-कवना जगह गइलें आ रहलें एकरे बारे में बहुत बिबरन ना मिले ला।{{sfn|Marino|2014|p=25}} अपना इंटरभ्यू में मोदी, स्वामी विवेकानंद के स्थापित बेलूर मठ (कलकत्ता लगे), एकरे बाद अल्मोड़ा के अद्वैत आश्रम आ रामकृष्ण मिशन, राजकोट जाए के बात कहले बाने। एह में से हर जगह कुछे दिन खातिर रह पवलें काहें कि उनके कॉलेज के शिक्षा अभिन पूरा ना भइल रहल।{{sfn|Marino|2014|pp=30–33}}{{sfn|Mukhopadhyay|2013|pp=128–129}}<ref>{{cite news|url=http://articles.economictimes.indiatimes.com/2014-05-26/news/50099180_1_narendra-modi-swami-vivekananda-belurmath|title=Narendra Modi invited to Ramakrishna Mission's headquarter in Belurmath|work=द इकॉनोमिक टाइम्स|date=26 मई 2014|accessdate=17 अक्टूबर 2014|archive-date=2014-10-31|archive-url=https://web.archive.org/web/20141031144902/http://articles.economictimes.indiatimes.com/2014-05-26/news/50099180_1_narendra-modi-swami-vivekananda-belurmath|url-status=dead}}</ref> विवेकानंद के इनके जीवन पर बहुत परभाव बतावल जाला।<ref name=ts0716>{{cite news |title=The tale of two Narendras: Narendra Modi and Swami Vivekananda |url=http://www.thestatesman.com/india/the-tale-of-two-narendras-narendra-modi-and-swami-vivekananda-152114.html|work=दि स्टेट्समैन |date=5 जुलाई 2016|accessdate=17 फरवरी 2017}}</ref>
1968 के गर्मी के सुरुआत में मोदी बेलूर मठ चहुँपलें बाकी इहाँ से वापस लवटा दिहल गइलें, जेकरा बाद कलकत्ता पच्छिम बंगाल आ आसाम में घूमलें, सिलीगुड़ी आ गौहाटी में रुकलें।{{sfn|Marino|2014|p=26}} मोदी अल्मोड़ा के रामकृष्ण आश्रम चहुँपलें आ एहू जा उनके ना लिहल गइल, एकरे बाद दिल्ली [[राजस्थान]] होखत वापस 1968–69 में गुजरात लवट अइलें।{{sfn|Marino|2014|p=27}} 1969 के अंत में या फिर 1970 के सुरुआत में मोदी कुछ दिन खातिर वडनगर अइलें आ एकरा बाद अहमदाबाद चल गइलें।{{sfn|Marino|2014|pp=28–29}} इहँवा मोदी अपने रिश्तेदार के इहाँ, गुजरात राज्य परिवहन कारपोरेशन के कैंटीन में काम करे लगलें।{{sfn|Mukhopadhyay|2013|p=131}}{{sfn|Mukhopadhyay|2013|p=138}}
अहमदाबाद में मोदी दोबारा इनामदार से दोबारा मिललें जे ओह समय शहर के हेगडेवार भवन (शहर के आरएसएस मुख्यालय) में रहलें।<ref name="vakilsaheb">{{cite web|last=Unnithan|first=Sandeep|title=The man behind Modi: Lakshmanrao Inamdar|url=http://indiatoday.intoday.in/story/narendra-modi-lakshmanrao-inamdar-rss/1/359926.html|work=इंडिया टुडे |location=Ahmedabad |year=2014 |accessdate=22 May 2014}}</ref><ref name="livemint-modi-growth">{{cite web|last=Mukhopadhyay|first=Nilanjan|title=Narendra Modi: The making of the political leader|url=http://www.livemint.com/Politics/9NIOOTsZCFWnuFX5OO9XNL/Narendra-Modi-The-making-of-the-political-leader.html |year=2014 |accessdate=22 May 2014}}</ref>{{sfn | Marino | 2014 |p=35}} 1971 के भारत-पाक लड़ाई के बाद मोदी अपना चाचा के साथे काम कइल छोड़ के पूरा टाइम खातिर आरएसएस के प्रचारक बन गइलें{{sfn|Mukhopadhyay|2013|p=138}} आ इनामदार के मातहत काम करे लगलें।{{sfn|Marino|2014|pp=35–40}} लड़ाई के कुछे दिन पहिले मोदी भारत सरकार के खिलाफ एगो शांतिपूर्ण धरना-परदर्शन में हिस्सा लिहलें जेकरा खातिर उनके गिरफ्तार कइल गइल रहे; इहे कारन बतावल जाला कि इनामदार इनके सिखावे-पढ़ावे खातिर बीछलें।{{sfn|Marino|2014|pp=35–40}} कई साल बाद 2001 में इनामदार के जीवनी छपल जेह में मोदी सहायक लेखक रहलें।<ref>{{cite news |title=Mentoring Modi |first=Prateek |last=Goyal |date=18 May 2014 |url=http://www.punemirror.in/pune/cover-story/Mentoring-Modi/articleshow/35270364.cms |work=Pune Mirror |accessdate=21 जून 2015}}</ref> साल 1978 में मोदी राजनीतिशास्त्र में बीए कइलें,<ref>{{cite news|url=http://indiatoday.intoday.in/story/narendra-modi-degree-controversy-delhi-university-rti/1/903745.html |title=Narendra Modi degree row: DU college says it has no data of students passing out in 1978|work=[[इंडिया टुडे]] |accessdate=9 जून 2017}}</ref> दिल्ली विश्वविद्यालय के पत्राचार माध्यम से,{{sfn | Marino | 2014 |p=48}} आ थर्ड डिवीजन पास भइलें।<ref>{{cite news|url=http://www.thehindu.com/news/cities/Delhi/Narendra-Modi%E2%80%99s-degree-%E2%80%98authentic%E2%80%99-Delhi-University-Registrar-Tarun-Das/article14311846.ece |title=Narendra Modi’s degree ‘authentic’: Delhi University Registrar Tarun Das|work=[[दि हिंदू]] |accessdate=9 जून 2017}}</ref> पाँच बरिस बाद, 1982 में, गुजरात विश्वविद्यालय से [[राजनीति बिज्ञान]] में एमए के डिग्री लिहलें।<ref>{{cite web|last=Avasthi|first=Yogesh|url=http://economictimes.indiatimes.com/news/politics-and-nation/globetrotting-pm-modi-was-weakest-in-international-relations/articleshow/52200607.cms|title=Globetrotting PM Modi was weakest in 'International Relations'|accessdate=17 February 2017}}</ref><ref>{{cite news |url=http://www.hindustantimes.com/india-news/modi-proves-to-be-an-astute-strategist/article1-264941.aspx |title=Modi proves to be an astute strategist |work=Hindustan Times |date=23 December 2007 |accessdate=17 April 2014 |archive-date=19 August 2014 |archive-url=https://web.archive.org/web/20140819083702/http://www.hindustantimes.com/india-news/modi-proves-to-be-an-astute-strategist/article1-264941.aspx |url-status=dead }}</ref>
2008 में, मोदी ज्योतिपुंज नाम के एगो गुजराती पुस्तक प्रकाशित कइलन, जेमें आरएसएस नेता लोगन के प्रोफाइल शामिल बा। सबसे लंबा समय तक एम. एस. गोलवलकर रहले, जेकरा नेतृत्व में आरएसएस के विस्तार भईल औरी जिनका के मोदी पूज्य श्री गुरुजी ("पूजा के योग्य गुरु") के रूप में संदर्भित करतरन।
==नोट==
<references group="नोट"/>
==संदर्भ==
{{Reflist|30em}}
==बाहरी कड़ी==
{{Commons category|Narendra Modi}}
;ऑफिशियल
* [http://pmindia.gov.in/en/ Official website (Prime Minister's office)]
* {{Official website|http://www.narendramodi.in}} (personal)
* [http://www.bjp.org/leadership/shri-narendra-modi Official page on Bharatiya Janata Party's website] {{Webarchive|url=https://web.archive.org/web/20181222221309/http://www.bjp.org/leadership/shri-narendra-modi |date=2018-12-22 }}
{{s-start}}
{{s-off}}
{{s-bef|before=केशुभाई पटेल}}
{{s-ttl|title=गुजरात के मुख्यमंत्री|years=2001–2014}}
{{s-aft|after=आनंदीबेन पटेल}}
|-
{{s-bef|before=[[मनमोहन सिंह]]}}
{{s-ttl|title=[[भारत के प्रधानमंत्री]]|years=2014–वर्तमान}}
{{s-inc}}
|-
{{s-par|in-lwr}}
{{s-bef|before=[[मुरली मनोहर जोशी]]}}
{{s-ttl|title=लोकसभा सांसद<br />बनारस से|years=2014–वर्तमान}}
{{s-inc}}
{{s-end}}
{{Authority control}}
[[श्रेणी:1950 में जनम]]
[[श्रेणी:16वीं लोकसभा के सदस्य]]
[[श्रेणी:गुजराती लोग]]
[[श्रेणी:जियत लोग]]
[[श्रेणी:उत्तर प्रदेश से लोकसभा सांसद]]
[[श्रेणी:नरेंद्र मोदी|*]]
[[श्रेणी:भारत के परधानमंत्री]]
evtgxugl89jaqjcbsg47fx1qs1helkz
गोदावरी नदी
0
9319
803338
796221
2026-07-29T16:21:53Z
SM7Bot
7808
/* बान्ह */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803338
wikitext
text/x-wiki
{{Geobox|River
<!-- *** Name section *** -->
| name = गोदावरी
| native_name =
| other_name = दक्षिण गंगा
<!-- *** Image *** --->
| image = Dummugudem Barrage on Godavari Khammam District.jpg
| image_size = 300
| image_caption = गोदावरी पर बनल एगो बंधा
<!-- *** Country etc. *** -->
| country = [[भारत]]
| state = [[महाराष्ट्र]]
| state1 = [[तेलंगाना]]
| state2 = [[छत्तीसगढ़]]
| state3 = [[आंध्र प्रदेश]]
| state4 = [[पांडिचेरी]] ([[यनम, पांडिचेरी|यनम]])
| region = [[दक्खिन भारत]], [[पच्छिम भारत]]
| city1 = [[नाशिक]]
| city2 = [[गंगाखेड़]]
| city3 = [[नांदेड़]]
| city4 = [[रामागुंडम]]
| city5 = [[भद्राचलम]]
| city6 = [[राजमुंद्री]]
<!-- *** Geography *** -->
| length = 1465
| length_round = 0
| watershed = 312812
| watershed_round = 0
| discharge1_location = पोलावरम (1901–1979)
| discharge1_average = 3061.18
| discharge1_max = 34606
| discharge1_min = 7
| discharge1_note = <ref>{{cite web|title=Sage River Database |url=http://www.sage.wisc.edu/riverdata/scripts/station_table.php?qual=32&filenum=865 |accessdate=16 जून 2011 |url-status=dead|archiveurl=https://web.archive.org/web/20100621153903/http://www.sage.wisc.edu/riverdata/scripts/station_table.php?qual=32&filenum=865 |archivedate=21 जून 2010 }}</ref>
| discharge_location =
| discharge_average = 3505
| discharge_note = <ref>{{cite journal | last = कुमार | first = राकेश | last2 = सिंह | first2 = आर. डी. | last3 = शर्मा | first3 = के. डी. | title = Water Resources of India | trans-title = भारत के जल संसाधन | journal = करेंट साइंस | volume = 89 | issue = 5 | pages = 794–811 | publisher = Current Science Association | location = बंगलौर | date = 10 सितंबर 2005 | url = http://www.currentscience.ac.in/Downloads/article_id_089_05_0794_0811_0.pdf | accessdate = 13 अक्टूबर 2013 | archive-date = 2019-01-06 | archive-url = https://web.archive.org/web/20190106043752/http://www.currentscience.ac.in/Downloads/article_id_089_05_0794_0811_0.pdf%20 | url-status = dead }}</ref>
<!-- *** Source *** -->
| source_name =
| source_location = ब्रह्मगिरि पहाड़ी, [[त्र्यंबकेश्वर]]
| source_district = [[नासिक]]
| source_region =
| source_state = [[महाराष्ट्र]]
| source_country = [[भारत]]
| source_coordinates = {{coord|19|55|48|N|73|31|39|E|display=inline}}
| source_elevation = 920
| source_length =
<!-- *** Mouth *** -->
| mouth_name =
| mouth_location = [[अंतर्वेदी]], [[बंगाल के खाड़ी]] में
| mouth_district = [[पूर्ब गोदावरी जिला|पूर्ब गोदावरी]]
| mouth_region =
| mouth_state = [[आंध्र प्रदेश]]
| mouth_country = [[भारत]]
| capital_coordinates =
| mouth_coordinates = {{coord|17|0|N|81|48|E|display=inline,title}}
| mouth_coordinates_note = <ref>{{GEOnet2|32FA87A287373774E0440003BA962ED3|Godāvari River}}</ref>
| mouth_elevation = 0
<!-- *** Tributaries*** -->
| tributary_left = [[बाणगंगा (महाराष्ट्र)|बाणगंगा]]
| tributary_left1 = [[कादवा नदी|कादवा]]
| tributary_left2 = [[शिवना नदी|शिवना]]
| tributary_left3 = [[पूर्णा नदी(गोदावरी थाला)|पूर्णा]]
| tributary_left4 = [[कदम नदी|कदम]]
| tributary_left5 = [[प्राणहिता नदी|प्राणहिता]]
| tributary_left6 = [[इंद्रावती नदी|इंद्रावती]]
| tributary_left7 = [[तालिपेरु नदी|तालिपेरु]]
| tributary_left8 = [[सबरी नदी|सबरी]]
| tributary_right = [[नासार्दी नदी|नासार्दी]]
| tributary_right1 = [[दरना नदी|दरना]]
| tributary_right2 = [[प्रवरा नदी|प्रवरा]]
| tributary_right3 = [[सिंदफाना नदी|सिंदफाना]]
| tributary_right4 = [[मांजरा नदी|मांजरा]]
| tributary_right5 = [[मनेर नदी|मनेर]]
| tributary_right6 = [[किन्नारासनी नदी|किन्नारासनी]]
| tributary_right7 =
<!-- *** Free free fields *** -->
| free_name =
| free_value =
<!-- *** Map section *** -->
| map = Godavari River.png
| map_size = 300 px
| map_caption = गोदावरी के रास्ता के नक्शा
<!-- *** Website *** -->
| website =
| commons =
<!-- *** Footnotes *** -->
| footnotes =
}}
'''गोदावरी''' नदी [[दक्खिन भारत]] में बहे वाली एगो महत्वपूर्ण नदी ह आ प्रायदीपी भारत के सभसे बड़ थाला वाली नदी ह। ई [[महाराष्ट्र]] से निकल के [[बंगाल के खाड़ी]] में गिरे ले। गोदावरी नदी भारत के दूसरी सबसे लंबी नदी ह, गंगा नदी के बाद। एकरा निकसे के सोता महाराष्ट्र के त्रयंबकेश्वर में बा।<ref>{{Cite web |url=http://www.kgbo-cwc.ap.nic.in/About%20Basins/Godavari.pdf |title="Godavari river basin map" |access-date=2018-06-22 |archive-date=2013-10-12 |archive-url=https://web.archive.org/web/20131012032632/http://www.kgbo-cwc.ap.nic.in/About%20Basins/Godavari.pdf |url-status=dead }}</ref> इहाँ से निकसे के बाद ई {{convert|1465|km|mi}} के दूरी तय करे ले जेकरे दौरान ई महाराष्ट्र (48.6 %), [[तेलंगाना]] (18.8 %), [[आंध्र प्रदेश]] (4.5 %), [[छत्तीसगढ़]] (10.9 %), [[मध्य प्रदेश]] (10.0 %), [[ओडिशा]] (5.7 %), [[कर्नाटक]] (1.4 %) आ [[पुद्दुचेरी]] ([[यनम जिला|यनम]]) से हो के गुजरे ले आ अंत में [[बंगाल के खाड़ी]] में अपने सहायिका सभ के बिसाल नेटवर्क से एकट्ठा कइल पानी गिरावे ले।<ref>{{cite web |url=http://www.cwc.nic.in/main/webpages/hba.pdf |title=Integrated Hydrological DataBook (Non-Classified River Basins) |publisher=Central Water Commission |accessdate=2015-10-13 |page=9 |archive-date=2016-03-04 |archive-url=https://web.archive.org/web/20160304192920/http://www.cwc.nic.in/main/webpages/hba.pdf |url-status=dead }}</ref> एक थाला (बेसिन) लगभग {{convert|3,12,812|km2|sqmi|abbr=on}} एरिया पर बिस्तार लिहले बा आ एह तरीका से ई भारतीय उपमहादीप के सभसे बिसाल नदी थाला सभ में से एक हवे; खाली गंगा आ सिंधु नदी के थाला एकरा ले बड़हन बाने।<ref>{{cite web|url=http://india-wris.nrsc.gov.in/wrpinfo/index.php?title=Basins |title=Basins |language=en |publisher=India-wris.nrsc.gov.in |date= |accessdate=2018-09-22}}</ref> लंबाई, थाला के साइज आ कुल पानी के बहाव, सभ मामिला में ई प्रायदीपी भारत के सभसे बड़ नदी ठहरे ले; मुला एही कारन एकरा के '''दक्खिन गंगा''' के उपाधि दिहल गइल होखी।<ref>{{cite web |url=http://www.importantindia.com/10222/dakshina-ganga/ |title=Dakshina Ganga (Ganga of South India) – River Godavari |publisher=Important India |accessdate=2015-10-21 |archive-date=2016-01-18 |archive-url=https://web.archive.org/web/20160118171303/http://www.importantindia.com/10222/dakshina-ganga/ |url-status=dead }}</ref>
हिंदू ग्रंथ सभ एह नदी के बरनन सदियन से मिले ला आ एकरे थाला में बहुत समृद्ध संस्कृति के बिकास भइल बा। पछिला कुछ दशक में, नदी के धारा रोक के कई गो बंधा बनावल गइल बाड़ें जिनहन से एकर बहाव कम भइल बा। एह नदी के डेल्टा वाला इलाका में जनघनत्व 729 ब्यक्ति/किमी<sup>2</sup> — देस के औसत के लगभग दुन्ना बाटे, आ ई डेल्टाई इलाका ख़तरा में भी बतावल जा रहल बा अगर [[समुंद्र तल]] में उठान होखे।<ref>http://www.igbp.net/download/18.62dc35801456272b46d4b/1398850074082/NL82-Deltas_infographic.pdf</ref><ref>{{cite web |url=http://www.indiaenvironmentportal.org.in/files/file/Shrinking_and_sinking_delta_major_role_of_Dams_May_2014.pdf |last=South Asia Network on Dams Rivers and People |title=Shrinking and Sinking Deltas: Major role of Dams in delta subsidence and effective sea level rise |year=2014 |accessdate=15 जनवरी 2016 |archive-date=2016-02-06 |archive-url=https://web.archive.org/web/20160206060736/http://www.indiaenvironmentportal.org.in/files/file/Shrinking_and_sinking_delta_major_role_of_Dams_May_2014.pdf |url-status=dead }}</ref>
== बहाव मारग ==
गोदावरी नदी [[महाराष्ट्र]] में [[नासिक]] के लगे [[पच्छिमी घाट]] के पहाड़ी से निकसे ले, एकर उदगम से अरब सागर बस के दूरी पर बाटे। ई नदी कुल के लंबाई तय करे ले, पहिले पूरुब मुँह के दक्कन के पठार में, फिर दक्खिन-पूरुब मुँह के हो जाले आ [[आंध्र प्रदेश]] के [[पच्छिम गोदावरी जिला]] आ [[पूर्ब गोदावरी जिला]] में प्रवेश करे ले, एकरा बाद आगे बढ़ के ई दू गो मुख्य धारा में बँट जाले आ बिसाल डेल्टा बनावे आ [[राजमुंद्री]] के लगे [[डोलेश्वरम बैराज]] के आगे [[बंगाल के खाड़ी]] में गिर जाले।
गोदावरी नदी के [[नदी थाला|थाला (बेसिन)]] के कुल रकबा करीबन बाटे , ई भारत देस के कुल रकबा के लगभग दसवाँ हिस्सा बाटे आ [[यूनाइटेड किंगडम]] आ [[आयरलैंड रिपब्लिक|आयरलैंड]] के मिला के दुनों के बरोबर बाटे। नदी के लंबाई के तीन खंड में बाँटल जाला:
* ऊपरी (मांजरा नदी से संगम से पहिले)
* बिचला (मांजरा के संगम से [[प्राणहिता नदी|प्राणहिता]] के संगम ले)
* निचला (प्राणहिता के संगम के बाद)
ई तीनों हिस्सा मिला के नदी के कुल बेसिन के 24.2 % हिस्सा होखे ला। एह नदी के सालाना बहाव बाटे आ लगभग आधा हिस्सा के बिबिध प्रोजेक्ट सभ द्वारा लाभ खातिर इस्तेमाल कइल जा रहल बाटे (जइसे कि पनबिजली खातिर)। एह नदी के पानी के राज्यन के बीचा में (रिपरियन स्टेट्स) बँटवारा गोदावरी वाटर डिस्प्यूट ट्रिब्यूनल द्वारा होखे ला। नदी के बहाव में बाढ़ वाला बहाव भारत के सभसे बेसी बाढ़ बहाव वालन में से एक हवे, 1986 में बाढ़ में ई बहाव 3.6 मिलियन क्यूसेक रिकार्ड कइल गइल रहे, 1 मिलियन क्यूसेक सालान नॉर्मल बाढ़ के बहाव होखे ला।
== सहायिका ==
गोदावरी नदी के मुख्य सहायिका नदी सभ के दू हिस्सा में बाँटल जा सके ला: बायें से आ के एह में पूर्णा, प्राणहिता, इंद्रावती आ सबरी नदी आ के मिले लीं के पूरा कैचमेंट एरिया के 59.7 % हिस्सा हवे, दाहिने से आ के मिले वाली नदी प्रवरा, मांजरा आ मनेर नदी बाड़ीं जे 16.1 % कैचमेंट एरिया कभर करे लीं।
[[प्राणहिता नदी]] एकर सभसे महत्व वाली सहायिका हवे आ एकरे कुल थाला के 34 % एही नदी के थाला हवे। भले प्राणहिता के लंबाई बस होखे, एकरे सहायिका सभ में वर्धा, वेणगंगा आ पेनगंगा नियर बिसाल नदी बाड़ी जे लगभग पूरा बिदर्भ प्रदेश आ सतपुड़ा पहाड़ के दक्खिनी ढाल कभर करे लीं। इंद्रावती एकर दुसरी सभसे बड़हन सहायिका हवे जे ओडिशा के कालाहांडी आ नबरंगपुर के आ छत्तीसगढ़ के बस्तर एरिया ले "लाइफलाइन" कहाले। प्राणहिता आ इंद्रावती के उप-बेसिन सभ के बिस्तार के चलते इनहन के अपने आप में नदी मानल जाला। मांजरा लंबाई में एकर सभसे बड़हन (सभसे लमहर) सहायिका हवे आ एह पर निजाम सागर बान्ह बाटे। पूर्णा एगो महत्त्व वाली सहायिका हवे जे महाराष्ट्र के मराठवाड़ा नियर पानी के कमी वाला एरिया से हो के बहे ले।
{{Pie chart
|thumb = right
|caption = '''Drainage basin of the Godavari'''<ref name="Godavari Basin">{{cite web|title=Hydrology and water resources information for India|url=http://www.nih.ernet.in/rbis/basin%20maps/godavari_about.htm|website=www.nih.ernet.in|publisher=National Institute of Hydrology, India|access-date=19 October 2015|url-status=dead|archive-url=https://web.archive.org/web/20150421200558/http://nih.ernet.in/rbis/basin%20maps/godavari_about.htm|archive-date=21 April 2015}}</ref>
|label1 = Upper, middle, and lower basins of the Godavari|color1 = #FF6600
|value1 = 24.2
|label2 = [[Pranhita River|Pranhita]]|color2 = #FFFF00
|value2 = 34.87
|label3 = [[Indravati River|Indravati]]|color3 = DodgerBlue
|value3 = 12.98
|label4 = [[Manjira River|Manjira]]|color4 = Green
|value4 = 9.86
|label5 = [[Sabari River|Sabari]]|color5 = Gold
|value5 = 6.53
|label6 = [[Purna River (tributary of Godavari)|Purna]]|color6 = Pink
|value6 = 4.98
|label7 = [[Maner River|Manair]] |color7 = Black
|value7 = 4.18
|label8 = [[Pravara River|Pravara]]|color8 = Gray
|value8 = 2.08
}}
{| class="wikitable sortable"
|+ Major tributaries of the Godavari river
|-
! Tributary !! Bank !! Confluence location !! Confluence elevation !! Length !! Sub-basin area
|-
| [[Pravara River|Pravara]] || Right || Pravara Sangam, [[Nevasa taluka|Nevasa]], [[Ahmednagar district|Ahmednagar]], [[Maharashtra]] || align="right" |{{convert|463|m|ft|abbr=on|disp=br()}} || align="right"|{{convert|208|km|mi|abbr=on|disp=br()}} || align="right"|{{convert|6,537|km2|sqmi|abbr=on|disp=br()}}
|-
| [[Purna River (tributary of Godavari)|Purna]] || Left || Jambulbet, [[Parbhani district|Parbhani]], [[Marathwada]], [[Maharashtra]] || align="right" |{{convert|358|m|ft|abbr=on|disp=br()}} || align="right"|{{convert|373|km|mi|abbr=on|disp=br()}} || align="right"|{{convert|15,579|km2|sqmi|abbr=on|disp=br()}}
|-
| [[Manjira River|Manjira]] || Right || Kandakurthi, Renjal, [[Nizamabad, Telangana]] || align="right"|{{convert|332|m|ft|abbr=on|disp=br()}} || align="right"|{{convert|724|km|mi|abbr=on|disp=br()}} || align="right"|{{convert|30,844|km2|sqmi|abbr=on|disp=br()}}
|-
| [[Manair River|Manair]] || Right || Arenda, [[Manthani]], [[Peddapalli district|Peddapalli]], [[Telangana]]|| align="right" |{{convert|115|m|ft|abbr=on|disp=br()}} || align="right"|{{convert|225|km|mi|abbr=on|disp=br()}} || align="right"|{{convert|13,106|km2|sqmi|abbr=on|disp=br()}}
|-
| [[Pranhita]] || Left || Kaleshwaram, [[Mahadevpur]], [[Jayashankar Bhupalpally]], [[Telangana]]|| align="right" |{{convert|99|m|ft|abbr=on|disp=br()}} || align="right"|{{convert|113|km|mi|abbr=on|disp=br()}} || align="right"|{{convert|109,078|km2|sqmi|abbr=on|disp=br()}}
|-
| [[Indravati]] || Left || Somnoor Sangam, [[Sironcha]], [[Gadchiroli district|Gadchiroli]], [[Maharashtra]] || align="right" |{{convert|82|m|ft|abbr=on|disp=br()}} || align="right"|{{convert|535|km|mi|abbr=on|disp=br()}} || align="right"|{{convert|41,655|km2|sqmi|abbr=on|disp=br()}}
|-
| [[Sabari River|Sabari]] || Left ||Kunawaram, [[East Godavari]], [[Andhra Pradesh]] || align="right" |{{convert|25|m|ft|abbr=on|disp=br()}} || align="right"|{{convert|418|km|mi|abbr=on|disp=br()}} || align="right"|{{convert|20,427|km2|sqmi|abbr=on|disp=br()}}
|}
Other than these 7 principal ones, it has many smaller but significant ones draining into it. Indravati river floodwaters overflow into the Jouranala which is part of Sabari basin. A barrage at {{Coord|19|7|19|N|82|14|9|E|name=Jouranala barrage}} is constructed across the Indravati river to divert Indravati water in to Sabari river for enhanced hydropower generation.
== बान्ह ==
प्रणहिता सहायक नदी के संगम तक ले मुख्य गोदावरी नदी के पूरा तरीका से बांध कइल गइल बा जेह से कि उपलब्ध पानी के सिंचाई खातिर इस्तेमाल कइल जा सके। हालाँकि, एकर मुख्य सहायक नदी प्रन्हिता, इंद्रवती आ शबरी जे बेसिन के निचला हिस्सा में जुड़ल बाड़ी, मुख्य गोदावरी के तुलना में तीन गुना ढेर पानी ले जालीं। साल 2015 में आंध्र प्रदेश में स्थित प्रकाशम बैराज में पानी के उपलब्धता बढ़ावे खातिर पट्टीसीमा लिफ्ट योजना के मदद से पोलावरम दाहिना किनारे के नहर के चालू क के पानी के अधिशेष गोदावरी नदी के जल घाटा वाला कृष्णा नदी से जोड़ल गइल बा। गोदावरी नदी बेसिन में भारत के कौनों अउरी नदी बेसिन के तुलना में ढेर बाँध सभ के निर्माण होला। नदी बेसिन में स्थित कुछ बांध निम्नलिखित बाड़ें:
* गंगापुर बाँध : ई एगो बड़हन मिट्टी भरल बाँध हवे जेह में सकल पानी के भंडारण 215.88 मिलियन घन मीटर बाटे, आ ई नासिक शहर से 10 किमी (6.2 मील) ऊपर के ओर स्थित बा। गंगापुर बंद सागर के नाँव से जानल जाए वाला ई जलाशय नासिक शहर के पीये के पानी उपलब्ध करावे ला आ एकलाहरे में नीचे के ओर स्थित थर्मल पावर स्टेशन के भी पानी के आपूर्ति करे ला।
* जयकवाड़ी बांध : पैठन के लगे स्थित इ भारत के सबसे बड़ माटी के बांध में से एक बा। ई बाँध किनारे के किनारे बाढ़ के दोहरी समस्या के समाधान खातिर बनावल गइल रहे, मानसून के महीना में, आ सूखा के, साल के बाकी हिस्सा में, मराठवाड़ा इलाका में। दू गो 'बाँया' आ 'दाहिना' नहर से नंदेद जिला तक के उपजाऊ जमीन के सिंचाई होला। ई बाँध औरंगाबाद आ जलना, महाराष्ट्र के औद्योगिक बिकास में योगदान देले बा।[29] परभनी, नंदेद आ बीड जिला में सिंचाई के संभावना के अउरी बढ़ावे खातिर मजलगांव बांध के निर्माण भी जयकवाड़ी स्टेज 2 के तहत कइल जाला।
* विष्णुपुरी बैराज: एशिया के सभसे बड़ लिफ्ट सिंचाई परियोजना, विष्णुपुरी प्रकाश[30] के निर्माण नदी पर नांदेद शहर से 5 किमी (3.1 मील) के दूरी पर कइल गइल बा।
* गोदावरी नदी बेसिन के बाहर प्रवरा सहायक नदी के पानी के एगो पश्चिम बहत नदी में मोड़ के जलविद्युत उत्पादन खातिर घाटघर बांध बनावल गइल जवन अरब सागर से जुड़ल बा।
* गोदावरी नदी जलग्रहण क्षेत्र के कुछ हिस्सा के मिला के पच्छिम में बहत वैतरना नदी के पार ऊपरी वैतरना जलाशय बनावल गइल। एह जलाशय में जब्त गोदावरी पानी के जलविद्युत पैदा कइला का बाद मुंबई शहर के पेयजल सप्लाई खातिर नदी बेसिन का बाहर डायवर्ट कर दिहल जाला.
* श्रीराम सागर बांध : आदिलाबाद आ निजामाबाद जिला के सीमा पर गोदावरी नदी पर ई एगो अउरी बहुउद्देशीय परियोजना बा। ई निजामाबाद से 60 किलोमीटर दूर पोचाम्पड शहर के लगे बा। एकरा के द हिंदू द्वारा "तेलंगाना के एगो बड़हन हिस्सा खातिर जीवन रेखा" बतावल गइल बा। ई करीमनगर, वारंगल, आदिलाबाद, नलगोंडा, आ खम्माम जिला सभ में सिंचाई के जरूरत के पूरा करे ला आ बिजली भी पैदा करे ला।
* सर आर्थर कॉटन बैराज के निर्माण सर आर्थर कॉटन द्वारा 1852 में कइल गइल।1987 के बाढ़ में एकरा के नुकसान भइल, आ एकरे तुरंत बाद बैराज सह रोडवे के रूप में फिर से बनावल गइल आ इनके नाँव पर एकर नाँव रखल गइल। ई सड़क मार्ग पूर्वी गोदावरी के राजामुंद्री आ पश्चिम गोदावरी के विजयेश्वरम के जोड़त बा। एह बैराज के सिंचाई नहर सभ भी राष्ट्रीय जलमार्ग 4 के हिस्सा हवें।
== संदर्भ ==
{{Reflist|33em}}
== बाहरी कड़ी ==
{{commons category|Godavari River}}
* [https://www.wisdomlib.org/hinduism/book/gautami-mahatmya Gautami Mahatmya (fourth book of the Brahma-purana)] English translation by G. P. Bhatt, 1955 (includes glossary)
* [http://www.sakti.in/godavaribasin/images/Godavari-sub-basins1.gif Godavari basin] {{Webarchive|url=https://web.archive.org/web/20210413202609/http://www.sakti.in/godavaribasin/images/Godavari-sub-basins1.gif |date=2021-04-13 }}
* [http://www.riversnetwork.org/rbo/index.php/river-blogs/south-asia/item/3985 Rivers Network: Godavari watersheds webmap] {{Webarchive|url=https://web.archive.org/web/20190928173430/http://www.riversnetwork.org/rbo/index.php/river-blogs/south-asia/item/3985 |date=28 September 2019 }}
* {{cite EB1911 |wstitle=Godavari (river) |display=Godavari |volume=12 |page=170 |short=1}}
* [https://web.archive.org/web/20040823082845/http://www.nashikdiary.com/ Nashik City]
* [http://adsabs.harvard.edu/abs/2001AGUSM...V22A07S Contrasting Behavior of Osmium in the Godavari River Estuary, India, 2001]
* [http://ams.allenpress.com/perlserv/?request=get-abstract&doi=10.1175%2F1520-0442(1998)011%3C0768:IVOMRI%3E2.0.CO%3B2 Variations of Monsoon Rainfall in Godavari River Basin]{{Dead link|date=May 2024 |bot=InternetArchiveBot |fix-attempted=yes }}
* [http://www.irfca.org/gallery/GeneralScenes/Godavari/ irfca.org]
{{Waters of South Asia}}
[[श्रेणी:भारत के नदी]]
[[श्रेणी:महाराष्ट्र के भूगोल]]
[[श्रेणी:तेलंगाना के भूगोल]]
j82kw5z5vslvec7vvltu05o63vxojpc
गुवाहाटी
0
9325
803324
766109
2026-07-29T16:20:57Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803324
wikitext
text/x-wiki
{{Infobox settlement
| name = गुवाहाटी
| native_name =
| other_name = गौहाटी(औपनिवेशिक)<br> प्रगज्योतिशपुरा(प्राचीन)
| settlement_type = [[महानगर]]
| image_skyline = {{Photomontage
| photo1a =
| photo2a = MG 2907-1 Guwahati Club.jpg
| photo2b = Guwahati Planetarium.jpg
| photo3a = Guwahati Railway Station at Night.jpg
| photo3b = Kamakhya Temple, Guwahati.jpg
| space = 1
| size = 280
| color = white
| color_border = white
}}
| image_alt = Guwahati collage.jpg
| image_caption = A view of Guwahati city
| image_seal =
| seal_alt =
| nickname = Gateway to Northeast India,<ref>{{cite web|url=https://www.theguardian.com/cities/2015/sep/28/insider-guide-guwahati-gateway-india-northeast |title=An insider's guide to Guwahati: more than just a gateway to India’s northeast| publisher= The Guardian|date= |accessdate=5 अक्टूबर 2015}}</ref> City of Temples, Light of the East<ref>{{cite web |url=http://www.kamrupmetro.nic.in/indexmain.asp |title=Kamrup Metro District |publisher=Kamrup(M) District Administration |accessdate=5 अक्टूबर 2015 |url-status=dead |archiveurl=https://web.archive.org/web/20150911190123/http://kamrupmetro.nic.in/indexmain.asp |archivedate=11 September 2015 |df=dmy-all }}</ref>
| image_map =
| map_alt =
| map_caption =
| pushpin_map = India Assam#India
| pushpin_label_position =
| pushpin_map_alt =
| pushpin_map_caption = Location of Guwahati in Assam
| coordinates = {{coord|26|11|N|91|44|E|display=inline,title}}
| subdivision_type = देश
| subdivision_name = {{Flag|भारत}}
| subdivision_type1 = राज्य
| subdivision_name1 = [[असम]]
| subdivision_type2 = क्षेत्र
| subdivision_name2 = निचला असम
| subdivision_type3 = जिला
| subdivision_name3 = [[Kamrup Metropolitan district]]
| established_title = <!-- Established -->
| established_date =
| founder =
| named_for =
| government_type = Republic
| governing_body = [[Guwahati Municipal Corporation]]
| leader_title1 = [[Mayor]]
| leader_name1 = Mrigen Sarania ([[Bharatiya Janata Party|BJP]])
| leader_title2 = Deputy Commissioner
| leader_name2 = Virendra Mittal, IAS<ref>{{cite web|url=http://cm.assam.gov.in/dc.php|title=Chief Minister of Assam - Deputy Commissioners|access-date=2018-09-10|archive-date=2018-05-10|archive-url=https://web.archive.org/web/20180510122644/http://cm.assam.gov.in/dc.php|url-status=dead}}</ref>
| leader_title3 =
Police Commissioner
| leader_name3 = Sri Hiren Chandra Nath, IPS<ref>{{cite web |url=http://guwahaticitypolice.gov.in/contact_us/senior_officers.php |title=Police Commissionerate Guwahati |publisher=Guwahati City Police |date= |accessdate=15 September 2015 |archive-date=6 September 2015 |archive-url=https://web.archive.org/web/20150906155742/http://guwahaticitypolice.gov.in/contact_us/senior_officers.php |url-status=dead }}</ref>
| area_footnotes = <ref name="The Telegraph">{{cite web|url=http://www.telegraphindia.com/1150101/jsp/northeast/story_6181.jsp#.VffzEfmqpBc |title= Magisterial powers for Guwahati top cop |publisher=The Telegraph |date= |accessdate=15 September 2015}}</ref>
| area_total_km2 = 328
| area_total_sq_mi = 128
| elevation_footnotes =
| elevation_m = 50-680 <!-- Google Earth elevation scale, from Pandu Ghat to Ahom Gaon near ISBT. Do not modify. Average elevation is a nonsensical measure for a hilly city and thus altitude extremeties carry valuable meaning. Similar is done with most hilly or mountainous cities on the planet. From Tehran to Los Angeles to Zurich to even Delhi. -->
| elevation_ft = 164-2231
| population_footnotes = <ref>{{cite web|url=http://www.census2011.co.in/census/city/191-guwahati.html |title= Guwahati City Census| publisher=censusindia.gov.in | date= |accessdate=15 September 2015}}</ref>
| population_total = 957352
| population_as_of = 2011
| population_rank = [[List of most populous cities in India|48th]]
| population_density_km2 = auto
| population_metro_footnotes =
| population_metro = 962,334 ([[urban agglomeration|UA]])
| population_demonym = Guwahatian
| timezone1 = [[भारतीय मानक समय|आइएसटी]]
| utc_offset1 = +5:30
| postal_code_type = [[Postal Index Number|PIN]]
| postal_code = 781 XXX
| area_code_type = Telephone code
| area_code = +91 - (0) 361 - XX XX XXX
| iso_code = [[ISO 3166-2:IN|IN-AS]]
| registration_plate = AS-01 (Kamrup Metro) / AS-25 (Kamrup Rural and Dispur)
| blank1_name_sec1 = [[Human Development Index|HDI]]
| blank1_info_sec1 = {{increase}} 0.725 {{color|#fc5| high}}<ref>{{cite web|url=http://www.okd.in/downloads/assam_hdr_30sep2016.pdf|title=आर्काइव कॉपी के बा|access-date=2018-09-10|archive-date=2017-03-22|archive-url=https://web.archive.org/web/20170322195446/http://okd.in/downloads/assam_hdr_30sep2016.pdf|url-status=dead}}</ref>
| blank2_name_sec1 = City animal
| blank2_info_sec1 = [[South Asian river dolphin|Gangetic river Dolphin]]<ref>{{cite web|url=http://m.timesofindia.com/home/environment/flora-fauna/Gangetic-river-dolphin-to-be-city-animal-of-Guwahati/articleshow/52623206.cms|title=Gangetic river dolphin to be city animal of Guwahati|work=[[दि टाइम्स ऑफ इंडिया]]|accessdate=14 जून 2017|date=6 जून 2017}}</ref>
| blank1_name_sec2 = {{nowrap|[[Official language]]}}
| blank1_info_sec2 = [[Assamese language|Assamese]] is city's official language used in governmental and conversation purpose , other languages spoken across city includes -: [[Bodo language|Bodo]], [[Bangla language|Bengali]], [[Hindi language|Hindi]], [[Mishing language|Mishing]], [[Rabha language|Rabha]], [[Kachari language|Kachari]], [[Meitei language|Meitei]], and [[Punjabi language|Punjabi]].
| blank2_name_sec2 = [[Literacy in India|Literacy]]
| blank2_info_sec2 = {{increase}} 91.47% {{color|#fc5|high}}<ref>{{cite web|url=http://www.census2011.co.in/census/city/191-guwahati.html |title=Guwahati City Census 2011 data | publisher=Census2011.co.in |date= |accessdate=2014-09-22}}</ref>
| blank3_name_sec2 = [[Climate of India|Climate]]
| blank3_info_sec2 = [[Climatic regions of India|Cwa]] {{small|([[Köppen climate classification|Köppen]])}}
| website = {{URL|www.gmcportal.in/}}
}}
'''गुवाहाटी''' चाहे '''गौहाटी''' पूर्वोत्तर भारत की [[आसाम]] में सभसे बड़ शहर आ मेट्रो एरिया हवे आ ई पूर्वोत्तर भारत के भी सभसे बड़ शहर हवे। असम के राजधानी [[दिसपुर]] गुवाहाटी के भीतर स्थित सर्किट सिटी क्षेत्र में बा आ असम सरकार के मुख्यालय ह। पहाड़ी सभ के साथ एगो प्रमुख नदी किनारे के बंदरगाह शहर, आ भारत के सभसे तेजी से बढ़त[12] शहर सभ में से एक, गुवाहाटी [[ब्रह्मपुत्र नदी|ब्रह्मपुत्र]] के दक्खिनी किनारे पर स्थित बा। एकरा के ''पूर्वोत्तर भारत के प्रवेश द्वार'' कहल जाला।
प्राचीन शहर प्रागज्योतिषपुरा आ दुर्जय (उत्तर गुवाहाटी) प्राचीन राज्य कामरूप के राजधानी रहलें। कई गो प्राचीन हिंदू मंदिर जइसे कि [[कामाख्या मंदिर]], उग्रतारा मंदिर, बसिष्ठ मंदिर, दोल गोविन्द मंदिर, उमानन्द मंदिर, नवग्रह मंदिर, सुखरेश्वर मंदिर, रुद्रेश्वर मंदिर, मणिकर्णेश्वर मंदिर, अश्वक्लांत मंदिर, दीर्घेश्वरी मंदिर, अश्वक्रांता मंदिर, लंकेश्वर मंदिर, भुवनेश्वरी मंदिर, शुभ .गणेश मंदिर, श्री पंचायतन मंदिर, नूनमाटी, आ अइसने कई गो मंदिर, शहर में बाड़ें जवना से एकरा के "मंदिर के शहर" के उपाधि दिहल गइल बा.
गुवाहाटी ब्रह्मपुत्र नदी के किनारे आ [[शिलॉंग पठार|शिलांग पठार]] के तलहटी के बीच में पड़े ला आ पच्छिम में एलजीबी अंतर्राष्ट्रीय हवाई अड्डा आ पूरब में नरेंगी शहर बा। ब्रह्मपुत्र के उत्तरी तट तक के उत्तर गुवाहाटी इलाका के धीरे-धीरे शहर के सीमा में शामिल कइल जा रहल बा। प्रख्यात मदन कामदेव गुवाहाटी से 30 किलोमीटर (19 मील) दूर स्थित बा। गुवाहाटी नगर निगम, शहर के स्थानीय सरकार, 216 वर्ग किलोमीटर (83 वर्ग मील) के क्षेत्रफल के प्रबंधन करे ले, जबकि गुवाहाटी महानगर बिकास प्राधिकरण (GMDA) ग्रेटर गुवाहाटी महानगर क्षेत्र के योजना आ बिकास निकाय हवे। गुवाहाटी पूर्वोत्तर भारत के सभसे बड़ शहर बा।
गुवाहाटी इलाका में बिबिध वन्यजीव सभ पावल जालें जेह में एशियाई हाथी, अजगर, बाघ, गैंडा, गौर, प्राइमेट प्रजाति सभ, आ बिलुप्त होखे वाला चिरई सभ नियर दुर्लभ जानवर सामिल बाड़ें।
{{clear}}
==संदर्भ==
{{Reflist|33em}}
{{भारत-भू-आधार}}
[[श्रेणी:भारत के शहर आ कस्बा]]
[[श्रेणी:पूर्बोत्तर भारत]]
ta5cxpzon267m8pvoukjxw7j587jmuz
उत्तर प्रदेश के नाच
0
9479
803358
795866
2026-07-29T16:23:24Z
SM7Bot
7808
/* ख़्याल नृत्य */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803358
wikitext
text/x-wiki
{{अनुवाद}}
'''[[लोकनाच]]''' में [[उत्तर प्रदेश]] के मय अंचल के आपन विशिष्ट चिन्हासी बा। समृद्ध विरासत के विविधता के संईतल ई आंगिक कलारूप [[लोक संस्कृति]] के प्रमुख वाहक बङुवे।
== ख़्याल नृत्य ==
'''ख़्याल नृत्य''' पुत्र जन्मोत्सव पे [[बुंदेलखण्ड]] में कइल जाला। एह में रंगीन कागज आ बांस के मदद से एगो [[मंदिर]] बनावल जाला आ ओकरा बाद ओकरा के माथा पे रख के नाच कइल जाला।<ref>{{Cite web |url=http://www.indiaheritage.org/perform/dance/dup.htm |title=आर्काइव कॉपी के बा |access-date=2014-06-25 |archive-date=2013-07-04 |archive-url=https://web.archive.org/web/20130704151826/http://www.indiaheritage.org/perform/dance/dup.htm |url-status=dead }}</ref>
== रास नृत्य ==
'''रास नृत्य''' [[ब्रज]] में [[रासलीला]] के दौरान किया जाता है। [[रासक दण्ड नृत्य]] भी इसी क्षेत्र का एक आकर्षक नृत्य है। <ref>{{Cite web |url=http://www.dancesofindia.co.in/folk-dances-india/uttar-pradesh/ |title=आर्काइव कॉपी के बा |access-date=2014-06-25 |archive-date=2012-06-15 |archive-url=https://web.archive.org/web/20120615003259/http://www.dancesofindia.co.in/folk-dances-india/uttar-pradesh/ |url-status=dead }}</ref>
== झूला नृत्य ==
'''झूला नृत्य''' भी ब्रज क्षेत्र का नृत्य है, जिसका आयोजन [[श्रावण]] मास में किया जाता है। इस नृत्य को इस क्षेत्र के मंदिरों में बड़े उल्लास के साथ किया जाता है।
== मयूर नृत्य ==
'''मयूर नृत्य''' भी ब्रज क्षेत्र का ही नृत्य है। इसमें नर्तक [[मोर]] के पंख से बने विशेष वस्त्र धारण करते हैं।
== धोबिया नृत्य ==
'''धोबिया नृत्य''' [[पूर्वांचल]] में प्रचलित है। यह नृत्य [[धोबी]] समुदाय द्वारा किया जाता है। इसके माध्यम से धोबी एवं गदहे के मध्य आजीविका संबंधों का भावप्रवण निरूपण किया जाता है।
== चरकुला नृत्य ==
'''चरकुला नृत्य''' ब्रज क्षेत्रवासियों द्वारा किया जाता है। इस [[घड़ा]] नृत्य में [[बैलगाड़ी]] अथवा [[रथ]] के पहिये पर कई घड़े रखे जाते हैं फिर उन्हें सिर पर रखकर नृत्य किया जाता है।
== कठफोड़वा नृत्य ==
'''कठघोड़वा नृत्य''' पूर्वांचल और बिहार-झारखंड के मगध क्षेत्र में माँगलिक अवसरों पर किया जाता है। इसमें एक नर्तक अन्य नर्तकों के घेरे के अंदर कृत्रिम घोड़ी पर बैठकर नृत्य करता है।
== जोगिनी नृत्य ==
'''जोगिनी नृत्य''' विशेषकर [[रामनवमी]] के अवसर पर किया जाता है। इसके अंतर्गत साधु या कोई अन्य पुरुष महिला का रूप धारण करके नृत्य करते हैं।
== धींवर नृत्य ==
'''धींवर नृत्य''' अनेक शुभ अवसरों पर विशेषकर [[कहार]] जाति के लोगों द्वारा आयोजित किया जाता है।
== शौरा नृत्य ==
'''शौरा या सैरा नृत्य''' [[बुंदेलखण्ड]] के [[कृषक]] अपनी फसलों को काटते समय हर्ष प्रकट करने के उद्देश्य से करते हैं।
== कर्मा नृत्य ==
'''कर्मा व शीला नृत्य''' [[सोनभद्र]] और [[मिर्जापुर]] के [[खखार]] आदिवासी समूह द्वारा आयोजित किया जाता है।
== पासी नृत्य ==
'''पासी नृत्य''' [[पासी]] जाति के लोगों द्वारा सात अलग अलग मुद्राओं की एक गति तथा एक ही लय में [[युद्ध]] की भाँति किया जाता है।
== घोड़ा नृत्य ==
'''घोड़ा नृत्य''' [[बुंदेलखण्ड]] में माँगलिक अवसरों पर बाजों की धुन पर घोड़ों द्वारा करवाया जाता है।
== धुरिया नृत्य ==
'''धुरिया नृत्य''' को [[बुंदेलखण्ड]] के प्रजापति ([[कुम्हार]]) स्त्री वेश धारण करके करते हैं।
== छोलिया नृत्य ==
'''छोलिया नृत्य''' [[राजपूत]] जाति के लोगों द्वारा विवाहोत्सव पर किया जाता है। इसे करते समय नर्तकों के एक हाथ में [[तलवार]] तथा दूसरे हाथ में [[ढाल]] होती है।
== छपेली नृत्य ==
'''छपेली नृत्य''' एक हाथ में [[रुमाल]] तथा दूसरे हाथ में [[दर्पण]] लेकर किया जाता है। इस के माध्यम से नर्तक आध्यात्मिक समुन्नति की कामना करते हैं।
== नटवरी नृत्य ==
'''नटवरी नृत्य''' [[पूर्वांचल]] क्षेत्र के [[अहीर|अहीरों]] द्वारा किया जाता है। यह नृत्य गीत व [[नक्कारा|नक्कारे]] के सुरों पर किया जाता है।
== देवी नृत्य ==
'''देवी नृत्य''' अधिकांशतः [[बुंदेलखण्ड]] में ही प्रचलित है। इस लोक नृत्य में एक नर्तक देवी का स्वरूप धारण कर अन्य नर्तकों के सामने खड़ा रहता है तथा उसके सम्मुख शेष सभी नर्तक नृत्य करते हैं।
== राई नृत्य ==
'''राई नृत्य''' [[बुंदेलखण्ड]] की महिलाओं द्वारा किया जाता है। यहाँ की महिलाएँ इस नृत्य को विशेषतः [[श्रीकृष्ण जन्माष्टमी]] के अवसर पर करती हैं। इसको मयूर की भाँति किया जाता है, इसीलिेए यह '''मयूर नृत्य''' भी कहलाता है।
== दीप नृत्य ==
[[बुंदेलखण्ड]] के अहीरों द्वारा अनेकानेक दीपकों को प्रज्ज्वलित कर किसी घड़े, कलश अथवा थाल में रखकर प्रज्ज्वलित दीपकों को सिर पर रखकर '''दीप नृत्य''' किया जाता है।
== पाई डण्डा नृत्य ==
'''पाई डण्डा नृत्य''' [[गुजरात]] के [[डाण्डिया नृत्य]] के समान है जो कि [[बुंदेलखण्ड]] के अहीरों द्वारा किया जाता है।
== कार्तिक नृत्य ==
'''कार्तिक नृत्य''' [[बुंदेलखण्ड]] क्षेत्र में [[कार्तिक माह]] में नर्तकों द्वारा [[श्रीकृष्ण]] तथा गोपियों का रूप धरकर किया जाता है।
== कलाबाज नृत्य ==
'''कलाबाज नृत्य''' [[अवध]] क्षेत्र के नर्तकों द्वारा किया जाता है। इस नृत्य में नर्तक मोरबाजा लेकर कच्ची घोड़ी पर बैठ कर नृत्य करते हैं।
== मोरपंख व दीवारी नृत्य ==
[[ब्रज]] की [[लट्ठमार होली]] की तरह [[बुंदेलखण्ड]] विशेषकर [[चित्रकूट]] में [[दीपावली के]] दिन ढोल-नगाड़े की तान पर आकर्षक वेशभूषा के साथ मोरपंखधारी लठैत एक दूसरे पर ताबड़तोड़ वार करते हुए '''मोरपंख व दीवारी नृ्त्य''' व खेल का प्रदर्शन करते हैं।
== ठडिया नृत्य ==
[[सोनभद्र]] व पड़ोसी जिलों में संतान की कामना पूरी होने पर '''ठडिया नृ्त्य''' का आयोजन [[सरस्वती]] के चरणों समर्पित होकर किया किया जाता है।
== चौलर नृत्य ==
[[मिर्जापुर]] और [[सोनभद्र]] आदि जिलों में '''चौलर नृत्य''' अच्छी वर्षा तथा अच्छी फसल की कामना पूर्ति हेतु किया जाता है।
== ढेढिया नृत्य ==
'''ढेढिया नृत्य''' का प्रचलन [[द्वाबा]] क्षेत्र में है। राम के लंका विजय के पश्चात वापस आने पर स्वागत में किया जाता है। इसमें सिरपर छिद्रयुक्त मिट्टी के बर्तन में दीपक रखकर किया जाता है।
== ढरकहरी नृत्य ==
[[सोनभद्र]] की जनजातियों द्वारा '''ढरकहरी नृत्य''' का आयोजन किया जाता है।
== संदर्भ ==
{{reflist}}
== बाहरी कड़ियाँ ==
* [http://www.indiaheritage.org/perform/dance/dup.htm'''भारत की विरासत'''] {{Webarchive|url=https://web.archive.org/web/20130704151826/http://www.indiaheritage.org/perform/dance/dup.htm |date=2013-07-04 }}
* [http://www.dancesofindia.co.in/folk-dances-india/uttar-pradesh/'''भारत के लोकनृत्य'''] {{Webarchive|url=https://web.archive.org/web/20120615003259/http://www.dancesofindia.co.in/folk-dances-india/uttar-pradesh/ |date=2012-06-15 }}
* [http://www.indiatourisminfo.net/uttar-pradesh/dance-in-uttar-pradesh/ "उत्तरप्रदेश के सभी लोक नृत्य की सूचि"] {{Webarchive|url=https://web.archive.org/web/20130402174037/http://www.indiatourisminfo.net/uttar-pradesh/dance-in-uttar-pradesh/ |date=2013-04-02 }}
[[श्रेणी:उत्तर प्रदेश क नाच]]
[[श्रेणी:उत्तर प्रदेश|नाच]]
[[श्रेणी:लोक नाच]]
8sh7nu3lsjcn0a1t13xsll8xzz6dwjf
खिचड़ी
0
9627
803319
756551
2026-07-29T16:20:36Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803319
wikitext
text/x-wiki
{{for2|एही नाँव के तिहुआर|[[खिचड़ी (तिहुआर)]] आ [[मकर संक्रांति]]}}
{{Infobox prepared food
| name = खिचड़ी
| image = [[File:Khichuri-edit.jpg#file|250px]]
| caption = खिचड़ी
| country = भारतीय उपमहाद्वीप
| region = [[भारत]], [[पाकिस्तान]], [[नेपाल]]
| creator =
| course =
| served =
| main_ingredient = चावल, दाल, चाट मसाला
| variations =
| calories =
| other =
}}
'''खिचड़ी''' एगो भोजन क चीज हवे जेवन दाल आ चावल (चाउर) से बनावल जाला| ''खिचड़ी'' शब्द क उत्पत्ति [[संस्कृत]] की खिच्चा शब्द से भइल बा, जेकर मतलब होला, चावल और फली क एगो पकवान।<ref>{{cite web|title=मास्टर चेफ (यू.एस. सीज़न 6)|url=https://en.wikipedia.org/wiki/MasterChef_(U.S._season_6)}}</ref>
==इतिहास==
सेलेकस क ग्रीक राजदूत उल्लेख कई न की दाल की साथ चावल दक्षिण एशिया में [[भारत]] की लोगन की बीच बहुत लोकप्रिय बा। मोरक्को क यात्री, इब्न बट्टुता 1350 की आसपास सिरका में रहले की दौरान, चावल और मूंग से बनल एगो बानगी के किशरी की रूप में उल्लेख कइले बा। अफानासीय निकितिन, एगो रूसी साहसिक जवन 15 वीं सदी में दक्षिण एशिया क यात्रा कइले रहे, ओकरी लेखन में खिचड़ी क वर्णन मिलेला। खिचड़ी मुग़ल साम्राज्य में बहुत लोकप्रिय रहे, खासकर जहांगीर की समय में। मुगल सम्राट [[अकबर]] की वज़ीर, अबू-फजल इब्न मुबारक द्वारा लिखित 16 वीं शताब्दी की दस्तावेज़ आईन-ए-अकबरी में, खिचड़ी की नुस्खा क उल्लेख बा, जवन की सात प्रकार क बा। अकबर, बीरबल और खिचड़ी क एगो अनोखा कहानीयो क जिक्र मिलेला।
==क्षेत्रीय रूपांतर==
[[पाकिस्तान]], [[नेपाल]] और भारत में खिचड़ी एगो बहुत लोकप्रिय पकवान ह। इ पकवान भारत, [[गुजरात]], [[बंगाल]], [[बिहार]], [[झारखंड]], उत्तर प्रदेश, ओडिशा और [[महाराष्ट्र]] जैसन कईगो राज्यन में व्यापक रूप से तैयार कइल जाला। फूलगोभी, आलू और हरा मटर जैसे सब्जियां सामान्य तौर पर एमें मिला दिहल जाला। तटीय महाराष्ट्र में एकर एगो लोकप्रिय प्रकार झींगा से बनेला। एकही बर्तन में बहुत आसानी से बनवले की सुविधा की चलते खिचड़ी एगो पसंदीदा कैम्प फायर बानगी ह।
अन्य कई क्षेत्र में, खिचड़ी के आमतौर प कढ़ी की साथ परोसल जाला, और अक्सर साथ पापड़ भी रहेला।
==संदर्भ==
<references/>
{{भोजन-आधार}}
[[श्रेणी:भारतीय खाना]]
ap1eehd9uohkxk6gfhdhz6oo5hebqni
भारतीय गिद्ध
0
10461
803357
780258
2026-07-29T16:23:18Z
SM7Bot
7808
/* आबादी में कमी */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803357
wikitext
text/x-wiki
{{speciesbox
| status = CR
| status_system = IUCN3.1
| status_ref = <ref name=iucn>{{cite iucn |author=BirdLife International |date=2017 |title=''Gyps indicus'' |page=e.T22729731A117875047 |access-date=19 November 2021}}</ref>
| image = Indian vulture (Gyps indicus) Photograph by Shantanu Kuveskar.jpg
| image_caption = Indian vulture sunbathing in [[Mhasla]], [[Maharashtra]]
| genus = Gyps
| species = indicus
| authority = ([[Giovanni Antonio Scopoli|Scopoli]], 1786)<ref>{{cite book |author=Scopoli, J. A. |year=1786–88 |title=Deliciae Flora et Fauna Insubricae Ticini. An account including new descriptions of the birds and mammals collected by Pierre Sonnerat on his voyages |location=London |publisher=C. J. Clay |pages=7–18 |chapter=Aves |chapter-url=https://archive.org/details/cu31924005326800/page/n13/mode/2up}}</ref>
| range_map = GypsBengalensisMap.svg
| range_map_caption = Distribution in purple
}}
'''भारतीय गिद्ध''' ({{Lang|en|Indian vulture}}, Gyps indicus) एगो पुरान दुनिया के गिद्ध हवे जे [[भारत]], [[पाकिस्तान]] आ [[नेपाल]] के मूल निवासी हवे। एकरा के 2002 से [[अंतर्राष्ट्रीय प्रकृति संरक्षण संघ|आईयूसीएन]] के रेड लिस्ट में गंभीर रूप से बिलुप्त होखे वाला के रूप में लिस्ट कइल गइल बाटे, काहें से कि एह में जनसंख्या में बहुत गिरावट आइल बा। भारतीय गिद्ध सभ के मौत डाइक्लोफेनाक जहर के कारण किडनी फेल होखे से भइल मानल जाला।<ref name=iucn /> एकर प्रजनन मुख्य रूप से मध्य आ प्रायद्वीपीय भारत के पहाड़ी चट्टान सभ पर होला।
एकरे रेंज के उत्तरी हिस्सा में मौजूद पातर-चोंच वाला गिद्ध ''जिप्स टेनुइरोस्ट्रिस'' के एगो अलग प्रजाति मानल जाला।
==बिबरन==
भारतीय गिद्ध मध्यम आकार के आ भारी-भरकम होला। एकर देह आ गुप्त पंख पीयर होला, एकर उड़ान पंख गहिराह रंग के होला। एकर पाँख चौड़ा होला आ पोंछ के पंख छोट होला। एकर माथा आ गर्दन लगभग गंजा होला, आ एकर नोक काफी लंबा होला। एकर लंबाई 81–103 सेमी (32–41 इंच) होला आ एकर पाँख के बिस्तार 1.96–2.38 मीटर (6.4–7.8 फीट) होला। मादा नर से छोट होखे लीं।<ref>{{cite book |last1=Ferguson-Lees |first1=J. |last2=Christie |first2=D. A. |name-list-style=amp |year=2001 |title=Raptors of the World |location=Boston, New York |publisher=Houghton Mifflin Harcourt |isbn=0618127623 |page=120 |chapter=Long-billed Vulture ''Gyps indicus'' |chapter-url=https://books.google.com/books?id=hlIztc05HTQC&pg=PA120}}</ref>
एकर वजन 5.5–6.3 किलोग्राम (12–14 पाउंड) होला। ई यूरेशियन ग्रिफन के तुलना में छोट आ कम भारी बनल बा। ई ओह प्रजाति से अलगा होला काहें से कि एकर शरीर कम बफ आ पाँख के आवरण होला। एकरे अलावा एकरा में ग्रिफन सभ द्वारा देखावल जाए वाला सफेद रंग के मीडियन गुप्त बार के भी कमी बा।<ref>{{cite web |url=http://www.peregrinefund.org/vulture_factsheet.asp#Long-billed%20Vulture, |title=The Peregrine Fund |publisher=The Peregrine Fund |date=2010 |access-date=2011-05-31 |archive-url=https://web.archive.org/web/20110526203258/http://www.peregrinefund.org/vulture_factsheet.asp#Long-billed%20Vulture, |archive-date=2011-05-26 |url-status=dead}}</ref>
== आवास आ बेहवार==
भारतीय गिद्ध मुख्य रूप से दक्खिन आ मध्य भारत के चट्टान सभ पर प्रजनन करे ला, बाकी [[राजस्थान]] में घोंसला बनावे खातिर पेड़ सभ के इस्तेमाल करे खातिर जानल जाला। ई मानव निर्मित ऊँच बिल्डिंग सभ पर भी प्रजनन क सके ला, जइसे कि चतुर्भुज मंदिर। बाकी गिद्ध सभ नियर ई एगो मेहतर हवे, ज्यादातर लाश सभ से भोजन करे ला, जेकरा के ई सवाना के ऊपर आ मनुष्य के निवास के आसपास उड़ के पावे ला। ई अक्सर झुंड में जमा हो जाला।
== इस्थिति आ कंजर्वेशन==
===आबादी में कमी===
भारतीय गिद्ध आ सफेद-रम्प वाला गिद्ध, जी. बेंगालेंसिस ([[बंगाल गिद्ध]]), दुनों प्रजाति सभ के ऊपर आबादी के कमी के खतरा रहल बाटे। बांग्लादेश, भारत आ नेपाल में इनहन के 99%–97% आबादी कम भइल दर्ज कइल गइल बाटे। 2000-2007 के बीच एह प्रजाति आ पतला बिल वाला गिद्ध के सालाना गिरावट के दर औसतन सोलह प्रतिशत से ढेर रहल।<ref name="BirdLife">{{ cite web |url=http://www.birdlife.org/datazone/speciesfactsheet.php?id=31029 | title = BirdLife Fact Sheet: Indian vulture | access-date = 11 August 2014 |publisher=[[BirdLife International]]}}</ref> एकर कारण पशु चिकित्सा दवाई डाइक्लोफेनाक से होखे वाला जहर के रूप में पहचानल गइल बा। डाइक्लोफेनाक एगो नॉन स्टेरॉयडल एंटी-इंफ्लेमेटरी दवाई (NSAID) ह अउरी जब इ काम करेवाला जानवर के दिहल जाला त इ जोड़ के दर्द के कम क सकता अउरी एहीसे एकरा ले जादे समय तक काम करत रह सके लें। मानल जाता कि इ दवाई गिद्ध मरे वाला मवेशी के मांस के संगे निगल लेले बाड़े, जवना के जीवन के अंतिम दिन में डाइक्लोफेनाक दिहल गइल रहे।
डाइक्लोफेनाक के चलते गिद्ध के कई प्रजाति में किडनी फेल हो जाला। मार्च 2006 में भारत सरकार डाइक्लोफेनाक के पशु चिकित्सा के इस्तेमाल पर रोक लगावे के समर्थन करे के घोषणा कइलस। एगो अउरी एनएसएआईडी मेलोक्सिकैम गिद्ध खातिर हानिरहित पावल गइल बा आ ई डाइक्लोफेनाक के स्वीकार्य विकल्प साबित होखे के चाहत रहल। जब मेलोक्सिकैम के उत्पादन बढ़ जाई त उम्मीद कइल जाला कि इ डाइक्लोफेनाक नियन सस्ता होई। अगस्त 2011 ले लगभग एक साल ले पशु चिकित्सा के इस्तेमाल पर रोक लगावे से पूरा भारत में डाइक्लोफेनाक के इस्तेमाल से रोकल ना गइल।<ref name="Express">{{cite news |url = http://archive.indianexpress.com/news/banned-diclofenac-still-kills-vultures/828303/ | title = Banned diclofenac still kills vultures |first = Raakhi | last = Jagga | date = 7 August 2011 | newspaper = Express India | access-date = 11 August 2014}}</ref> पूरा प्रायदीपीय भारत में, कर्नाटक आ तमिलनाडु में, खासतौर पर बैंगलोर के आसपास के गाँव सभ में, कम संख्या में चिरई सभ के प्रजनन भइल बा।<ref name="Hindu">{{cite news|url=http://www.thehindu.com/news/national/tamil-nadu/longbilled-vultures-sighted-after-40-years/article5322851.ece|title=Long-billed Vulture sighted after 40 years|newspaper=The Hindu|date=7 November 2013 |first = P. |last = Oppili | access-date = 11 August 2014}}</ref> भारतीय गिद्ध के गिरावट से पर्यावरण के संरक्षण प बहुत असर पड़ल बा। सभ लाश सभ के हटा के गिद्ध सभ प्रदूषण के कम करे, बेमारी के फइलल आ अवांछित स्तनधारी सभ के मेहतर सभ के दबावे में मदद कइले रहलें।<ref>{{Cite journal|last1=Prakash|first1=V. |last2=Bishwakarma|first2=M. C. |last3=Chaudhary |first3=A. |last4=Cuthbert |first4=R. |last5=Dave |first5=R. |last6=Kulkarni |first6=M. |last7=Kumar |first7=S. |last8=Paudel |first8=K. |last9=Ranade |first9=S. |name-list-style=amp |date=2012 |title=The Population Decline of Gyps Vultures in India and Nepal Has Slowed since Veterinary Use of Diclofenac was Banned |journal=PLOS ONE |volume=7 |issue=11 |pages=e49118 |doi=10.1371/journal.pone.0049118 |pmc=3492300 |pmid=23145090 |bibcode=2012PLoSO...749118P|doi-access=free }}</ref> इनहन के अनुपस्थिति में जंगली कुकुरन आ चूहा सभ के आबादी के साथे-साथ इनहन के जूनोटिक बेमारी सभ में भी बहुत बढ़ती भइल बा।<ref>{{cite journal |last1=Prakash |first1=V. |last2=Pain |first2=D.J. |last3=Cunningham |first3=A.A. |last4=Donald |first4=P.F. |last5=Prakash |first5=N. |last6=Verma |first6=A. |last7=Gargi |first7=R. |last8=Sivakumar |first8=S. |last9=Rahmani |first9=A.R. |name-list-style=amp |title=Catastrophic collapse of Indian white-backed ''Gyps bengalensis'' and long-billed ''Gyps indicus'' vulture populations |journal=Biological Conservation |date=2003 |volume=109 |issue=3 |pages=381–390 |doi=10.1016/S0006-3207(02)00164-7}}</ref>
===कैप्टिव-ब्रीडिंग ===
[[File:Vultures in the nest, Orchha, MP, India edit.jpg|thumb|Vultures in a nest in [[Orchha]]]]
भारतीय गिद्ध के कई गो प्रजाति खातिर कैप्टिव-ब्रिडिंग कार्यक्रम (बंधक राखि के प्रजनन) शुरू हो गइल बा। गिद्ध सभ लंबा उमिर के होलें आ प्रजनन में धीमा होलें, एह से एह कार्यक्रम सभ में दशक भर के समय लागे के उमेद बा। गिद्ध लगभग पाँच साल के उमिर में प्रजनन के उमिर में पहुँच जालें। उम्मीद बा कि जब पर्यावरण में डाइक्लोफेनाक से साफ हो जाई त कैद में पोसल चिरई के जंगल में छोड़ दिहल जाई।
साल 2014 के सुरुआत में "सेविंग एशिया के गिद्ध" नाँव से गिद्ध सभ के बिलुप्त होखे से बचावे के कार्यक्रम घोषणा कइलस कि एकरा उमेद बा कि 2016 ले कैप्टिव-बर्ड चिरई सभ के जंगल में छोड़ल शुरू क दिहल जाई।<ref>{{cite news|last=Kinver|first=M. |title=Project targets 2016 for Asian vultures release|url=https://www.bbc.co.uk/news/science-environment-25963100|access-date=2 February 2014 |newspaper=BBC News|date=31 Jan 2014}}</ref>
एशिया के पहिला गिद्ध के पुनर्परिचय कार्यक्रम के हिस्सा के रूप में जून, 2016 में जटायू संरक्षण प्रजनन केंद्र, पिंजौर से दू गो कैप्टिव हिमालयन ग्रिफन के रिहा कइल गइल।
==इहो देखल जाय==
*[[बंगाल गिद्ध]]
*[[सफ़ेद गिद्ध]]
== संदर्भ ==
{{Reflist|33em}}
== बाहरी कड़ी ==
{{Commons category|Gyps indicus}}
{{Wikispecies|Gyps indicus}}
* [https://web.archive.org/web/20150722223425/http://www.vulturerescue.org/ Conserving Asia's critically endangered vultures]
* [http://www.vulture-territory.com/billed.html Vulture Territory Facts and Characteristics: Long Billed Griffon]
* [http://www.save-vultures.org/ "Saving Asia's Vultures from Extinction" Consortium]
* [https://web.archive.org/web/20170107044924/http://www.snmcpn.org/vultures/indian-white-backed-vulture Conservation of Vultures in Konkan region]
<!-- {{Vulture}}-->
{{Taxonbar|from=Q248086}}
[[श्रेणी:भारत के चिरई]]
[[श्रेणी:गिद्ध]]
lp64d5z3qp8bbpg3lcghlnqn8nmbj11
Module:Check for unknown parameters
828
14742
803635
778426
2025-10-09T18:33:27Z
en>MSGJ
0
add option to bulk-include mapframe parameters, code by [[User:Zackmann08]]
803635
Scribunto
text/plain
-- This module may be used to compare the arguments passed to the parent
-- with a list of arguments, returning a specified result if an argument is
-- not on the list
require ('strict');
local p = {}
local mapframe_params = {
'mapframe',
'mapframe-area_km2',
'mapframe-area_mi2',
'mapframe-caption',
'mapframe-coord',
'mapframe-coordinates',
'mapframe-custom',
'mapframe-frame-coord',
'mapframe-frame-coordinates',
'mapframe-frame-height',
'mapframe-frame-width',
'mapframe-geomask',
'mapframe-geomask-fill',
'mapframe-geomask-fill-opacity',
'mapframe-geomask-stroke-color',
'mapframe-geomask-stroke-colour',
'mapframe-geomask-stroke-width',
'mapframe-height',
'mapframe-id',
'mapframe-length_km',
'mapframe-length_mi',
'mapframe-marker',
'mapframe-marker-color',
'mapframe-marker-colour',
'mapframe-point',
'mapframe-population',
'mapframe-shape',
'mapframe-shape-fill',
'mapframe-shape-fill-opacity',
'mapframe-stroke-color',
'mapframe-stroke-colour',
'mapframe-stroke-width',
'mapframe-switcher',
'mapframe-type',
'mapframe-width',
'mapframe-wikidata',
'mapframe-zoom'
}
local function trim(s)
return s:match('^%s*(.-)%s*$')
end
local function isnotempty(s)
return s and s:match('%S')
end
local function clean(text)
-- Return text cleaned for display and truncated if too long.
-- Strip markers are replaced with dummy text representing the original wikitext.
local pos, truncated
local function truncate(text)
if truncated then
return ''
end
if mw.ustring.len(text) > 25 then
truncated = true
text = mw.ustring.sub(text, 1, 25) .. '...'
end
return mw.text.nowiki(text)
end
local parts = {}
for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do
pos = remainder
table.insert(parts, truncate(before) .. '<' .. tag .. '>...</' .. tag .. '>')
end
table.insert(parts, truncate(text:sub(pos or 1)))
return table.concat(parts)
end
function p._check(args, pargs)
if type(args) ~= "table" or type(pargs) ~= "table" then
-- TODO: error handling
return
end
-- create the list of known args, regular expressions, and the return string
local knownargs = {}
if isnotempty(args['mapframe_args']) then
for _, v in ipairs(mapframe_params) do
knownargs[v] = 1
end
end
local regexps = {}
for k, v in pairs(args) do
if type(k) == 'number' then
v = trim(v)
knownargs[v] = 1
elseif k:find('^regexp[1-9][0-9]*$') then
table.insert(regexps, '^' .. v .. '$')
end
end
-- loop over the parent args, and make sure they are on the list
local ignoreblank = isnotempty(args['ignoreblank'])
local showblankpos = isnotempty(args['showblankpositional'])
local values = {}
for k, v in pairs(pargs) do
if type(k) == 'string' and knownargs[k] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(k, regexp) then
knownflag = true
break
end
end
if not knownflag and ( not ignoreblank or isnotempty(v) ) then
table.insert(values, clean(k))
end
elseif type(k) == 'number' and knownargs[tostring(k)] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(tostring(k), regexp) then
knownflag = true
break
end
end
if not knownflag and ( showblankpos or isnotempty(v) ) then
table.insert(values, k .. ' = ' .. clean(v))
end
end
end
-- add results to the output tables
local res = {}
if #values > 0 then
local unknown_text = args['unknown'] or 'Found _VALUE_, '
if mw.getCurrentFrame():preprocess( "{{REVISIONID}}" ) == "" then
local preview_text = args['preview']
if isnotempty(preview_text) then
preview_text = require('Module:If preview')._warning({preview_text})
elseif preview_text == nil then
preview_text = unknown_text
end
unknown_text = preview_text
end
for _, v in pairs(values) do
-- Fix odd bug for | = which gets stripped to the empty string and
-- breaks category links
if v == '' then v = ' ' end
-- avoid error with v = 'example%2' ("invalid capture index")
local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v})
table.insert(res, r)
end
end
return table.concat(res)
end
function p.check(frame)
local args = frame.args
local pargs = frame:getParent().args
return p._check(args, pargs)
end
return p
1hjemu98zajwimptzieho9nxzbrn6ir
803636
803635
2025-10-17T10:40:59Z
en>Joy
0
document line and shape stroke-* params for mapframe
803636
Scribunto
text/plain
-- This module may be used to compare the arguments passed to the parent
-- with a list of arguments, returning a specified result if an argument is
-- not on the list
require ('strict');
local p = {}
local mapframe_params = {
'mapframe',
'mapframe-area_km2',
'mapframe-area_mi2',
'mapframe-caption',
'mapframe-coord',
'mapframe-coordinates',
'mapframe-custom',
'mapframe-frame-coord',
'mapframe-frame-coordinates',
'mapframe-frame-height',
'mapframe-frame-width',
'mapframe-geomask',
'mapframe-geomask-fill',
'mapframe-geomask-fill-opacity',
'mapframe-geomask-stroke-color',
'mapframe-geomask-stroke-colour',
'mapframe-geomask-stroke-width',
'mapframe-height',
'mapframe-id',
'mapframe-length_km',
'mapframe-length_mi',
'mapframe-line-stroke-color',
'mapframe-line-stroke-colour',
'mapframe-marker',
'mapframe-marker-color',
'mapframe-marker-colour',
'mapframe-point',
'mapframe-population',
'mapframe-shape',
'mapframe-shape-fill',
'mapframe-shape-fill-opacity',
'mapframe-shape-stroke-color',
'mapframe-shape-stroke-colour',
'mapframe-stroke-color',
'mapframe-stroke-colour',
'mapframe-stroke-width',
'mapframe-switcher',
'mapframe-type',
'mapframe-width',
'mapframe-wikidata',
'mapframe-zoom'
}
local function trim(s)
return s:match('^%s*(.-)%s*$')
end
local function isnotempty(s)
return s and s:match('%S')
end
local function clean(text)
-- Return text cleaned for display and truncated if too long.
-- Strip markers are replaced with dummy text representing the original wikitext.
local pos, truncated
local function truncate(text)
if truncated then
return ''
end
if mw.ustring.len(text) > 25 then
truncated = true
text = mw.ustring.sub(text, 1, 25) .. '...'
end
return mw.text.nowiki(text)
end
local parts = {}
for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do
pos = remainder
table.insert(parts, truncate(before) .. '<' .. tag .. '>...</' .. tag .. '>')
end
table.insert(parts, truncate(text:sub(pos or 1)))
return table.concat(parts)
end
function p._check(args, pargs)
if type(args) ~= "table" or type(pargs) ~= "table" then
-- TODO: error handling
return
end
-- create the list of known args, regular expressions, and the return string
local knownargs = {}
if isnotempty(args['mapframe_args']) then
for _, v in ipairs(mapframe_params) do
knownargs[v] = 1
end
end
local regexps = {}
for k, v in pairs(args) do
if type(k) == 'number' then
v = trim(v)
knownargs[v] = 1
elseif k:find('^regexp[1-9][0-9]*$') then
table.insert(regexps, '^' .. v .. '$')
end
end
-- loop over the parent args, and make sure they are on the list
local ignoreblank = isnotempty(args['ignoreblank'])
local showblankpos = isnotempty(args['showblankpositional'])
local values = {}
for k, v in pairs(pargs) do
if type(k) == 'string' and knownargs[k] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(k, regexp) then
knownflag = true
break
end
end
if not knownflag and ( not ignoreblank or isnotempty(v) ) then
table.insert(values, clean(k))
end
elseif type(k) == 'number' and knownargs[tostring(k)] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(tostring(k), regexp) then
knownflag = true
break
end
end
if not knownflag and ( showblankpos or isnotempty(v) ) then
table.insert(values, k .. ' = ' .. clean(v))
end
end
end
-- add results to the output tables
local res = {}
if #values > 0 then
local unknown_text = args['unknown'] or 'Found _VALUE_, '
if mw.getCurrentFrame():preprocess( "{{REVISIONID}}" ) == "" then
local preview_text = args['preview']
if isnotempty(preview_text) then
preview_text = require('Module:If preview')._warning({preview_text})
elseif preview_text == nil then
preview_text = unknown_text
end
unknown_text = preview_text
end
for _, v in pairs(values) do
-- Fix odd bug for | = which gets stripped to the empty string and
-- breaks category links
if v == '' then v = ' ' end
-- avoid error with v = 'example%2' ("invalid capture index")
local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v})
table.insert(res, r)
end
end
return table.concat(res)
end
function p.check(frame)
local args = frame.args
local pargs = frame:getParent().args
return p._check(args, pargs)
end
return p
eo62jat5hd6a7jz1ec339pjhpcal720
803637
803636
2025-10-17T10:42:12Z
en>Joy
0
document missing mapframe-line parameter
803637
Scribunto
text/plain
-- This module may be used to compare the arguments passed to the parent
-- with a list of arguments, returning a specified result if an argument is
-- not on the list
require ('strict');
local p = {}
local mapframe_params = {
'mapframe',
'mapframe-area_km2',
'mapframe-area_mi2',
'mapframe-caption',
'mapframe-coord',
'mapframe-coordinates',
'mapframe-custom',
'mapframe-frame-coord',
'mapframe-frame-coordinates',
'mapframe-frame-height',
'mapframe-frame-width',
'mapframe-geomask',
'mapframe-geomask-fill',
'mapframe-geomask-fill-opacity',
'mapframe-geomask-stroke-color',
'mapframe-geomask-stroke-colour',
'mapframe-geomask-stroke-width',
'mapframe-height',
'mapframe-id',
'mapframe-length_km',
'mapframe-length_mi',
'mapframe-line',
'mapframe-line-stroke-color',
'mapframe-line-stroke-colour',
'mapframe-marker',
'mapframe-marker-color',
'mapframe-marker-colour',
'mapframe-point',
'mapframe-population',
'mapframe-shape',
'mapframe-shape-fill',
'mapframe-shape-fill-opacity',
'mapframe-shape-stroke-color',
'mapframe-shape-stroke-colour',
'mapframe-stroke-color',
'mapframe-stroke-colour',
'mapframe-stroke-width',
'mapframe-switcher',
'mapframe-type',
'mapframe-width',
'mapframe-wikidata',
'mapframe-zoom'
}
local function trim(s)
return s:match('^%s*(.-)%s*$')
end
local function isnotempty(s)
return s and s:match('%S')
end
local function clean(text)
-- Return text cleaned for display and truncated if too long.
-- Strip markers are replaced with dummy text representing the original wikitext.
local pos, truncated
local function truncate(text)
if truncated then
return ''
end
if mw.ustring.len(text) > 25 then
truncated = true
text = mw.ustring.sub(text, 1, 25) .. '...'
end
return mw.text.nowiki(text)
end
local parts = {}
for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do
pos = remainder
table.insert(parts, truncate(before) .. '<' .. tag .. '>...</' .. tag .. '>')
end
table.insert(parts, truncate(text:sub(pos or 1)))
return table.concat(parts)
end
function p._check(args, pargs)
if type(args) ~= "table" or type(pargs) ~= "table" then
-- TODO: error handling
return
end
-- create the list of known args, regular expressions, and the return string
local knownargs = {}
if isnotempty(args['mapframe_args']) then
for _, v in ipairs(mapframe_params) do
knownargs[v] = 1
end
end
local regexps = {}
for k, v in pairs(args) do
if type(k) == 'number' then
v = trim(v)
knownargs[v] = 1
elseif k:find('^regexp[1-9][0-9]*$') then
table.insert(regexps, '^' .. v .. '$')
end
end
-- loop over the parent args, and make sure they are on the list
local ignoreblank = isnotempty(args['ignoreblank'])
local showblankpos = isnotempty(args['showblankpositional'])
local values = {}
for k, v in pairs(pargs) do
if type(k) == 'string' and knownargs[k] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(k, regexp) then
knownflag = true
break
end
end
if not knownflag and ( not ignoreblank or isnotempty(v) ) then
table.insert(values, clean(k))
end
elseif type(k) == 'number' and knownargs[tostring(k)] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(tostring(k), regexp) then
knownflag = true
break
end
end
if not knownflag and ( showblankpos or isnotempty(v) ) then
table.insert(values, k .. ' = ' .. clean(v))
end
end
end
-- add results to the output tables
local res = {}
if #values > 0 then
local unknown_text = args['unknown'] or 'Found _VALUE_, '
if mw.getCurrentFrame():preprocess( "{{REVISIONID}}" ) == "" then
local preview_text = args['preview']
if isnotempty(preview_text) then
preview_text = require('Module:If preview')._warning({preview_text})
elseif preview_text == nil then
preview_text = unknown_text
end
unknown_text = preview_text
end
for _, v in pairs(values) do
-- Fix odd bug for | = which gets stripped to the empty string and
-- breaks category links
if v == '' then v = ' ' end
-- avoid error with v = 'example%2' ("invalid capture index")
local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v})
table.insert(res, r)
end
end
return table.concat(res)
end
function p.check(frame)
local args = frame.args
local pargs = frame:getParent().args
return p._check(args, pargs)
end
return p
2l8d1a5bs6d84ovkw5hud1zhit3uisw
803638
803637
2025-10-17T11:01:38Z
en>Joy
0
document missing mapframe parameter aliases
803638
Scribunto
text/plain
-- This module may be used to compare the arguments passed to the parent
-- with a list of arguments, returning a specified result if an argument is
-- not on the list
require ('strict');
local p = {}
local mapframe_params = {
'coord',
'coordinates',
'id',
'qid',
'mapframe',
'mapframe-area_km2',
'mapframe-area_mi2',
'mapframe-caption',
'mapframe-coord',
'mapframe-coordinates',
'mapframe-custom',
'mapframe-frame-coord',
'mapframe-frame-coordinates',
'mapframe-frame-height',
'mapframe-frame-width',
'mapframe-geomask',
'mapframe-geomask-fill',
'mapframe-geomask-fill-opacity',
'mapframe-geomask-stroke-color',
'mapframe-geomask-stroke-colour',
'mapframe-geomask-stroke-width',
'mapframe-height',
'mapframe-id',
'mapframe-length_km',
'mapframe-length_mi',
'mapframe-line',
'mapframe-line-stroke-color',
'mapframe-line-stroke-colour',
'mapframe-marker',
'mapframe-marker-color',
'mapframe-marker-colour',
'mapframe-point',
'mapframe-population',
'mapframe-shape',
'mapframe-shape-fill',
'mapframe-shape-fill-opacity',
'mapframe-shape-stroke-color',
'mapframe-shape-stroke-colour',
'mapframe-stroke-color',
'mapframe-stroke-colour',
'mapframe-stroke-width',
'mapframe-switcher',
'mapframe-type',
'mapframe-width',
'mapframe-wikidata',
'mapframe-zoom'
}
local function trim(s)
return s:match('^%s*(.-)%s*$')
end
local function isnotempty(s)
return s and s:match('%S')
end
local function clean(text)
-- Return text cleaned for display and truncated if too long.
-- Strip markers are replaced with dummy text representing the original wikitext.
local pos, truncated
local function truncate(text)
if truncated then
return ''
end
if mw.ustring.len(text) > 25 then
truncated = true
text = mw.ustring.sub(text, 1, 25) .. '...'
end
return mw.text.nowiki(text)
end
local parts = {}
for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do
pos = remainder
table.insert(parts, truncate(before) .. '<' .. tag .. '>...</' .. tag .. '>')
end
table.insert(parts, truncate(text:sub(pos or 1)))
return table.concat(parts)
end
function p._check(args, pargs)
if type(args) ~= "table" or type(pargs) ~= "table" then
-- TODO: error handling
return
end
-- create the list of known args, regular expressions, and the return string
local knownargs = {}
if isnotempty(args['mapframe_args']) then
for _, v in ipairs(mapframe_params) do
knownargs[v] = 1
end
end
local regexps = {}
for k, v in pairs(args) do
if type(k) == 'number' then
v = trim(v)
knownargs[v] = 1
elseif k:find('^regexp[1-9][0-9]*$') then
table.insert(regexps, '^' .. v .. '$')
end
end
-- loop over the parent args, and make sure they are on the list
local ignoreblank = isnotempty(args['ignoreblank'])
local showblankpos = isnotempty(args['showblankpositional'])
local values = {}
for k, v in pairs(pargs) do
if type(k) == 'string' and knownargs[k] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(k, regexp) then
knownflag = true
break
end
end
if not knownflag and ( not ignoreblank or isnotempty(v) ) then
table.insert(values, clean(k))
end
elseif type(k) == 'number' and knownargs[tostring(k)] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(tostring(k), regexp) then
knownflag = true
break
end
end
if not knownflag and ( showblankpos or isnotempty(v) ) then
table.insert(values, k .. ' = ' .. clean(v))
end
end
end
-- add results to the output tables
local res = {}
if #values > 0 then
local unknown_text = args['unknown'] or 'Found _VALUE_, '
if mw.getCurrentFrame():preprocess( "{{REVISIONID}}" ) == "" then
local preview_text = args['preview']
if isnotempty(preview_text) then
preview_text = require('Module:If preview')._warning({preview_text})
elseif preview_text == nil then
preview_text = unknown_text
end
unknown_text = preview_text
end
for _, v in pairs(values) do
-- Fix odd bug for | = which gets stripped to the empty string and
-- breaks category links
if v == '' then v = ' ' end
-- avoid error with v = 'example%2' ("invalid capture index")
local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v})
table.insert(res, r)
end
end
return table.concat(res)
end
function p.check(frame)
local args = frame.args
local pargs = frame:getParent().args
return p._check(args, pargs)
end
return p
mf5u017ry3j5wcsi9ctzrqfash3oih5
803639
803638
2026-06-28T21:41:54Z
en>MSGJ
0
add list of pushpin map parameters
803639
Scribunto
text/plain
-- This module may be used to compare the arguments passed to the parent
-- with a list of arguments, returning a specified result if an argument is
-- not on the list
require ('strict');
local p = {}
local pushpin_map_params = {
'coordinates',
'pushpin_caption',
'pushpin_relief',
'pushpin_label',
'pushpin_label_position',
'pushpin_label_size',
'pushpin_mark',
'pushpin_mark_size',
'pushpin_alt',
'pushpin_background',
'pushpin_map_size'
}
local mapframe_params = {
'coord',
'coordinates',
'id',
'qid',
'mapframe',
'mapframe-area_km2',
'mapframe-area_mi2',
'mapframe-caption',
'mapframe-coord',
'mapframe-coordinates',
'mapframe-custom',
'mapframe-frame-coord',
'mapframe-frame-coordinates',
'mapframe-frame-height',
'mapframe-frame-width',
'mapframe-geomask',
'mapframe-geomask-fill',
'mapframe-geomask-fill-opacity',
'mapframe-geomask-stroke-color',
'mapframe-geomask-stroke-colour',
'mapframe-geomask-stroke-width',
'mapframe-height',
'mapframe-id',
'mapframe-length_km',
'mapframe-length_mi',
'mapframe-line',
'mapframe-line-stroke-color',
'mapframe-line-stroke-colour',
'mapframe-marker',
'mapframe-marker-color',
'mapframe-marker-colour',
'mapframe-point',
'mapframe-population',
'mapframe-shape',
'mapframe-shape-fill',
'mapframe-shape-fill-opacity',
'mapframe-shape-stroke-color',
'mapframe-shape-stroke-colour',
'mapframe-stroke-color',
'mapframe-stroke-colour',
'mapframe-stroke-width',
'mapframe-switcher',
'mapframe-type',
'mapframe-width',
'mapframe-wikidata',
'mapframe-zoom'
}
local function trim(s)
return s:match('^%s*(.-)%s*$')
end
local function isnotempty(s)
return s and s:match('%S')
end
local function clean(text)
-- Return text cleaned for display and truncated if too long.
-- Strip markers are replaced with dummy text representing the original wikitext.
local pos, truncated
local function truncate(text)
if truncated then
return ''
end
if mw.ustring.len(text) > 25 then
truncated = true
text = mw.ustring.sub(text, 1, 25) .. '...'
end
return mw.text.nowiki(text)
end
local parts = {}
for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do
pos = remainder
table.insert(parts, truncate(before) .. '<' .. tag .. '>...</' .. tag .. '>')
end
table.insert(parts, truncate(text:sub(pos or 1)))
return table.concat(parts)
end
function p._check(args, pargs)
if type(args) ~= "table" or type(pargs) ~= "table" then
-- TODO: error handling
return
end
-- create the list of known args, regular expressions, and the return string
local knownargs = {}
if isnotempty(args['mapframe_args']) then
for _, v in ipairs(mapframe_params) do
knownargs[v] = 1
end
end
if isnotempty(args['pushpin_map_args']) then
for _, v in ipairs(pushpin_map_params) do
knownargs[v] = 1
end
end
local regexps = {}
for k, v in pairs(args) do
if type(k) == 'number' then
v = trim(v)
knownargs[v] = 1
elseif k:find('^regexp[1-9][0-9]*$') then
table.insert(regexps, '^' .. v .. '$')
end
end
-- loop over the parent args, and make sure they are on the list
local ignoreblank = isnotempty(args['ignoreblank'])
local showblankpos = isnotempty(args['showblankpositional'])
local values = {}
for k, v in pairs(pargs) do
if type(k) == 'string' and knownargs[k] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(k, regexp) then
knownflag = true
break
end
end
if not knownflag and ( not ignoreblank or isnotempty(v) ) then
table.insert(values, clean(k))
end
elseif type(k) == 'number' and knownargs[tostring(k)] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(tostring(k), regexp) then
knownflag = true
break
end
end
if not knownflag and ( showblankpos or isnotempty(v) ) then
table.insert(values, k .. ' = ' .. clean(v))
end
end
end
-- add results to the output tables
local res = {}
if #values > 0 then
local unknown_text = args['unknown'] or 'Found _VALUE_, '
if mw.getCurrentFrame():preprocess( "{{REVISIONID}}" ) == "" then
local preview_text = args['preview']
if isnotempty(preview_text) then
preview_text = require('Module:If preview')._warning({preview_text})
elseif preview_text == nil then
preview_text = unknown_text
end
unknown_text = preview_text
end
for _, v in pairs(values) do
-- Fix odd bug for | = which gets stripped to the empty string and
-- breaks category links
if v == '' then v = ' ' end
-- avoid error with v = 'example%2' ("invalid capture index")
local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v})
table.insert(res, r)
end
end
return table.concat(res)
end
function p.check(frame)
local args = frame.args
local pargs = frame:getParent().args
return p._check(args, pargs)
end
return p
civhrks6bzupi1qw5hmyrd22dludbl6
803640
803639
2026-06-28T22:16:03Z
en>MSGJ
0
+pushpin_map
803640
Scribunto
text/plain
-- This module may be used to compare the arguments passed to the parent
-- with a list of arguments, returning a specified result if an argument is
-- not on the list
require ('strict');
local p = {}
local pushpin_map_params = {
'coordinates',
'pushpin_caption',
'pushpin_relief',
'pushpin_label',
'pushpin_label_position',
'pushpin_label_size',
'pushpin_map',
'pushpin_mark',
'pushpin_mark_size',
'pushpin_alt',
'pushpin_background',
'pushpin_map_size'
}
local mapframe_params = {
'coord',
'coordinates',
'id',
'qid',
'mapframe',
'mapframe-area_km2',
'mapframe-area_mi2',
'mapframe-caption',
'mapframe-coord',
'mapframe-coordinates',
'mapframe-custom',
'mapframe-frame-coord',
'mapframe-frame-coordinates',
'mapframe-frame-height',
'mapframe-frame-width',
'mapframe-geomask',
'mapframe-geomask-fill',
'mapframe-geomask-fill-opacity',
'mapframe-geomask-stroke-color',
'mapframe-geomask-stroke-colour',
'mapframe-geomask-stroke-width',
'mapframe-height',
'mapframe-id',
'mapframe-length_km',
'mapframe-length_mi',
'mapframe-line',
'mapframe-line-stroke-color',
'mapframe-line-stroke-colour',
'mapframe-marker',
'mapframe-marker-color',
'mapframe-marker-colour',
'mapframe-point',
'mapframe-population',
'mapframe-shape',
'mapframe-shape-fill',
'mapframe-shape-fill-opacity',
'mapframe-shape-stroke-color',
'mapframe-shape-stroke-colour',
'mapframe-stroke-color',
'mapframe-stroke-colour',
'mapframe-stroke-width',
'mapframe-switcher',
'mapframe-type',
'mapframe-width',
'mapframe-wikidata',
'mapframe-zoom'
}
local function trim(s)
return s:match('^%s*(.-)%s*$')
end
local function isnotempty(s)
return s and s:match('%S')
end
local function clean(text)
-- Return text cleaned for display and truncated if too long.
-- Strip markers are replaced with dummy text representing the original wikitext.
local pos, truncated
local function truncate(text)
if truncated then
return ''
end
if mw.ustring.len(text) > 25 then
truncated = true
text = mw.ustring.sub(text, 1, 25) .. '...'
end
return mw.text.nowiki(text)
end
local parts = {}
for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do
pos = remainder
table.insert(parts, truncate(before) .. '<' .. tag .. '>...</' .. tag .. '>')
end
table.insert(parts, truncate(text:sub(pos or 1)))
return table.concat(parts)
end
function p._check(args, pargs)
if type(args) ~= "table" or type(pargs) ~= "table" then
-- TODO: error handling
return
end
-- create the list of known args, regular expressions, and the return string
local knownargs = {}
if isnotempty(args['mapframe_args']) then
for _, v in ipairs(mapframe_params) do
knownargs[v] = 1
end
end
if isnotempty(args['pushpin_map_args']) then
for _, v in ipairs(pushpin_map_params) do
knownargs[v] = 1
end
end
local regexps = {}
for k, v in pairs(args) do
if type(k) == 'number' then
v = trim(v)
knownargs[v] = 1
elseif k:find('^regexp[1-9][0-9]*$') then
table.insert(regexps, '^' .. v .. '$')
end
end
-- loop over the parent args, and make sure they are on the list
local ignoreblank = isnotempty(args['ignoreblank'])
local showblankpos = isnotempty(args['showblankpositional'])
local values = {}
for k, v in pairs(pargs) do
if type(k) == 'string' and knownargs[k] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(k, regexp) then
knownflag = true
break
end
end
if not knownflag and ( not ignoreblank or isnotempty(v) ) then
table.insert(values, clean(k))
end
elseif type(k) == 'number' and knownargs[tostring(k)] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(tostring(k), regexp) then
knownflag = true
break
end
end
if not knownflag and ( showblankpos or isnotempty(v) ) then
table.insert(values, k .. ' = ' .. clean(v))
end
end
end
-- add results to the output tables
local res = {}
if #values > 0 then
local unknown_text = args['unknown'] or 'Found _VALUE_, '
if mw.getCurrentFrame():preprocess( "{{REVISIONID}}" ) == "" then
local preview_text = args['preview']
if isnotempty(preview_text) then
preview_text = require('Module:If preview')._warning({preview_text})
elseif preview_text == nil then
preview_text = unknown_text
end
unknown_text = preview_text
end
for _, v in pairs(values) do
-- Fix odd bug for | = which gets stripped to the empty string and
-- breaks category links
if v == '' then v = ' ' end
-- avoid error with v = 'example%2' ("invalid capture index")
local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v})
table.insert(res, r)
end
end
return table.concat(res)
end
function p.check(frame)
local args = frame.args
local pargs = frame:getParent().args
return p._check(args, pargs)
end
return p
kohqf9fxewyce9gq1kljbz0180u8sl1
803641
803640
2026-07-30T00:20:32Z
SM7
3953
6 revisions imported from [[:en:Module:Check_for_unknown_parameters]]
803640
Scribunto
text/plain
-- This module may be used to compare the arguments passed to the parent
-- with a list of arguments, returning a specified result if an argument is
-- not on the list
require ('strict');
local p = {}
local pushpin_map_params = {
'coordinates',
'pushpin_caption',
'pushpin_relief',
'pushpin_label',
'pushpin_label_position',
'pushpin_label_size',
'pushpin_map',
'pushpin_mark',
'pushpin_mark_size',
'pushpin_alt',
'pushpin_background',
'pushpin_map_size'
}
local mapframe_params = {
'coord',
'coordinates',
'id',
'qid',
'mapframe',
'mapframe-area_km2',
'mapframe-area_mi2',
'mapframe-caption',
'mapframe-coord',
'mapframe-coordinates',
'mapframe-custom',
'mapframe-frame-coord',
'mapframe-frame-coordinates',
'mapframe-frame-height',
'mapframe-frame-width',
'mapframe-geomask',
'mapframe-geomask-fill',
'mapframe-geomask-fill-opacity',
'mapframe-geomask-stroke-color',
'mapframe-geomask-stroke-colour',
'mapframe-geomask-stroke-width',
'mapframe-height',
'mapframe-id',
'mapframe-length_km',
'mapframe-length_mi',
'mapframe-line',
'mapframe-line-stroke-color',
'mapframe-line-stroke-colour',
'mapframe-marker',
'mapframe-marker-color',
'mapframe-marker-colour',
'mapframe-point',
'mapframe-population',
'mapframe-shape',
'mapframe-shape-fill',
'mapframe-shape-fill-opacity',
'mapframe-shape-stroke-color',
'mapframe-shape-stroke-colour',
'mapframe-stroke-color',
'mapframe-stroke-colour',
'mapframe-stroke-width',
'mapframe-switcher',
'mapframe-type',
'mapframe-width',
'mapframe-wikidata',
'mapframe-zoom'
}
local function trim(s)
return s:match('^%s*(.-)%s*$')
end
local function isnotempty(s)
return s and s:match('%S')
end
local function clean(text)
-- Return text cleaned for display and truncated if too long.
-- Strip markers are replaced with dummy text representing the original wikitext.
local pos, truncated
local function truncate(text)
if truncated then
return ''
end
if mw.ustring.len(text) > 25 then
truncated = true
text = mw.ustring.sub(text, 1, 25) .. '...'
end
return mw.text.nowiki(text)
end
local parts = {}
for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do
pos = remainder
table.insert(parts, truncate(before) .. '<' .. tag .. '>...</' .. tag .. '>')
end
table.insert(parts, truncate(text:sub(pos or 1)))
return table.concat(parts)
end
function p._check(args, pargs)
if type(args) ~= "table" or type(pargs) ~= "table" then
-- TODO: error handling
return
end
-- create the list of known args, regular expressions, and the return string
local knownargs = {}
if isnotempty(args['mapframe_args']) then
for _, v in ipairs(mapframe_params) do
knownargs[v] = 1
end
end
if isnotempty(args['pushpin_map_args']) then
for _, v in ipairs(pushpin_map_params) do
knownargs[v] = 1
end
end
local regexps = {}
for k, v in pairs(args) do
if type(k) == 'number' then
v = trim(v)
knownargs[v] = 1
elseif k:find('^regexp[1-9][0-9]*$') then
table.insert(regexps, '^' .. v .. '$')
end
end
-- loop over the parent args, and make sure they are on the list
local ignoreblank = isnotempty(args['ignoreblank'])
local showblankpos = isnotempty(args['showblankpositional'])
local values = {}
for k, v in pairs(pargs) do
if type(k) == 'string' and knownargs[k] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(k, regexp) then
knownflag = true
break
end
end
if not knownflag and ( not ignoreblank or isnotempty(v) ) then
table.insert(values, clean(k))
end
elseif type(k) == 'number' and knownargs[tostring(k)] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(tostring(k), regexp) then
knownflag = true
break
end
end
if not knownflag and ( showblankpos or isnotempty(v) ) then
table.insert(values, k .. ' = ' .. clean(v))
end
end
end
-- add results to the output tables
local res = {}
if #values > 0 then
local unknown_text = args['unknown'] or 'Found _VALUE_, '
if mw.getCurrentFrame():preprocess( "{{REVISIONID}}" ) == "" then
local preview_text = args['preview']
if isnotempty(preview_text) then
preview_text = require('Module:If preview')._warning({preview_text})
elseif preview_text == nil then
preview_text = unknown_text
end
unknown_text = preview_text
end
for _, v in pairs(values) do
-- Fix odd bug for | = which gets stripped to the empty string and
-- breaks category links
if v == '' then v = ' ' end
-- avoid error with v = 'example%2' ("invalid capture index")
local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v})
table.insert(res, r)
end
end
return table.concat(res)
end
function p.check(frame)
local args = frame.args
local pargs = frame:getParent().args
return p._check(args, pargs)
end
return p
kohqf9fxewyce9gq1kljbz0180u8sl1
टेम्पलेट:PAGENAMEBASE
10
15955
803652
141331
2016-03-06T09:37:56Z
en>Cabayi
0
per edit request & discussion
803652
wikitext
text/x-wiki
{{{{{|safesubst:}}}#Invoke:String|replace|{{{1|{{{{{|safesubst:}}}PAGENAME}}}}}|%s+%b()$||1|false}}<noinclude>
{{documentation}}
</noinclude>
sam8aetb68ekom4findu6pp900ngldr
803653
297013
2024-10-28T10:45:21Z
en>Dinoguy1000
0
fix "|=foo" bug
803653
wikitext
text/x-wiki
{{safesubst:<noinclude/>#invoke:String|replace|{{{1|{{safesubst:<noinclude/>PAGENAME}}}}}|%s+%b()$||1|false}}<noinclude>
{{documentation}}
</noinclude>
hfmr5rmcuhdncvzny8x6n6ob8hddcpq
803654
803653
2026-07-30T00:22:05Z
SM7
3953
2 revisions imported from [[:en:Template:PAGENAMEBASE]]
803653
wikitext
text/x-wiki
{{safesubst:<noinclude/>#invoke:String|replace|{{{1|{{safesubst:<noinclude/>PAGENAME}}}}}|%s+%b()$||1|false}}<noinclude>
{{documentation}}
</noinclude>
hfmr5rmcuhdncvzny8x6n6ob8hddcpq
Module:String
828
15958
803564
778438
2025-08-11T23:47:14Z
en>Pppery
0
Copy findpagetext from [[Module:String2]]
803564
Scribunto
text/plain
--[[
This module is intended to provide access to basic string functions.
Most of the functions provided here can be invoked with named parameters,
unnamed parameters, or a mixture. If named parameters are used, Mediawiki will
automatically remove any leading or trailing whitespace from the parameter.
Depending on the intended use, it may be advantageous to either preserve or
remove such whitespace.
Global options
ignore_errors: If set to 'true' or 1, any error condition will result in
an empty string being returned rather than an error message.
error_category: If an error occurs, specifies the name of a category to
include with the error message. The default category is
[Category:Errors reported by Module String].
no_category: If set to 'true' or 1, no category will be added if an error
is generated.
Unit tests for this module are available at Module:String/tests.
]]
local str = {}
--[[
len
This function returns the length of the target string.
Usage:
{{#invoke:String|len|target_string|}}
OR
{{#invoke:String|len|s=target_string}}
Parameters
s: The string whose length to report
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the target string.
]]
function str.len( frame )
local new_args = str._getParameters( frame.args, {'s'} )
local s = new_args['s'] or ''
return mw.ustring.len( s )
end
--[[
sub
This function returns a substring of the target string at specified indices.
Usage:
{{#invoke:String|sub|target_string|start_index|end_index}}
OR
{{#invoke:String|sub|s=target_string|i=start_index|j=end_index}}
Parameters
s: The string to return a subset of
i: The first index of the substring to return, defaults to 1.
j: The last index of the string to return, defaults to the last character.
The first character of the string is assigned an index of 1. If either i or j
is a negative value, it is interpreted the same as selecting a character by
counting from the end of the string. Hence, a value of -1 is the same as
selecting the last character of the string.
If the requested indices are out of range for the given string, an error is
reported.
]]
function str.sub( frame )
local new_args = str._getParameters( frame.args, { 's', 'i', 'j' } )
local s = new_args['s'] or ''
local i = tonumber( new_args['i'] ) or 1
local j = tonumber( new_args['j'] ) or -1
local len = mw.ustring.len( s )
-- Convert negatives for range checking
if i < 0 then
i = len + i + 1
end
if j < 0 then
j = len + j + 1
end
if i > len or j > len or i < 1 or j < 1 then
return str._error( 'String subset index out of range' )
end
if j < i then
return str._error( 'String subset indices out of order' )
end
return mw.ustring.sub( s, i, j )
end
--[[
This function implements that features of {{str sub old}} and is kept in order
to maintain these older templates.
]]
function str.sublength( frame )
local i = tonumber( frame.args.i ) or 0
local len = tonumber( frame.args.len )
return mw.ustring.sub( frame.args.s, i + 1, len and ( i + len ) )
end
--[[
_match
This function returns a substring from the source string that matches a
specified pattern. It is exported for use in other modules
Usage:
strmatch = require("Module:String")._match
sresult = strmatch( s, pattern, start, match, plain, nomatch )
Parameters
s: The string to search
pattern: The pattern or string to find within the string
start: The index within the source string to start the search. The first
character of the string has index 1. Defaults to 1.
match: In some cases it may be possible to make multiple matches on a single
string. This specifies which match to return, where the first match is
match= 1. If a negative number is specified then a match is returned
counting from the last match. Hence match = -1 is the same as requesting
the last match. Defaults to 1.
plain: A flag indicating that the pattern should be understood as plain
text. Defaults to false.
nomatch: If no match is found, output the "nomatch" value rather than an error.
For information on constructing Lua patterns, a form of [regular expression], see:
* http://www.lua.org/manual/5.1/manual.html#5.4.1
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns
]]
-- This sub-routine is exported for use in other modules
function str._match( s, pattern, start, match_index, plain_flag, nomatch )
if s == '' then
return str._error( 'Target string is empty' )
end
if pattern == '' then
return str._error( 'Pattern string is empty' )
end
start = tonumber(start) or 1
if math.abs(start) < 1 or math.abs(start) > mw.ustring.len( s ) then
return str._error( 'Requested start is out of range' )
end
if match_index == 0 then
return str._error( 'Match index is out of range' )
end
if plain_flag then
pattern = str._escapePattern( pattern )
end
local result
if match_index == 1 then
-- Find first match is simple case
result = mw.ustring.match( s, pattern, start )
else
if start > 1 then
s = mw.ustring.sub( s, start )
end
local iterator = mw.ustring.gmatch(s, pattern)
if match_index > 0 then
-- Forward search
for w in iterator do
match_index = match_index - 1
if match_index == 0 then
result = w
break
end
end
else
-- Reverse search
local result_table = {}
local count = 1
for w in iterator do
result_table[count] = w
count = count + 1
end
result = result_table[ count + match_index ]
end
end
if result == nil then
if nomatch == nil then
return str._error( 'Match not found' )
else
return nomatch
end
else
return result
end
end
--[[
match
This function returns a substring from the source string that matches a
specified pattern.
Usage:
{{#invoke:String|match|source_string|pattern_string|start_index|match_number|plain_flag|nomatch_output}}
OR
{{#invoke:String|match|s=source_string|pattern=pattern_string|start=start_index
|match=match_number|plain=plain_flag|nomatch=nomatch_output}}
Parameters
s: The string to search
pattern: The pattern or string to find within the string
start: The index within the source string to start the search. The first
character of the string has index 1. Defaults to 1.
match: In some cases it may be possible to make multiple matches on a single
string. This specifies which match to return, where the first match is
match= 1. If a negative number is specified then a match is returned
counting from the last match. Hence match = -1 is the same as requesting
the last match. Defaults to 1.
plain: A flag indicating that the pattern should be understood as plain
text. Defaults to false.
nomatch: If no match is found, output the "nomatch" value rather than an error.
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from each string. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
If the match_number or start_index are out of range for the string being queried, then
this function generates an error. An error is also generated if no match is found.
If one adds the parameter ignore_errors=true, then the error will be suppressed and
an empty string will be returned on any failure.
For information on constructing Lua patterns, a form of [regular expression], see:
* http://www.lua.org/manual/5.1/manual.html#5.4.1
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns
]]
-- This is the entry point for #invoke:String|match
function str.match( frame )
local new_args = str._getParameters( frame.args, {'s', 'pattern', 'start', 'match', 'plain', 'nomatch'} )
local s = new_args['s'] or ''
local start = tonumber( new_args['start'] ) or 1
local plain_flag = str._getBoolean( new_args['plain'] or false )
local pattern = new_args['pattern'] or ''
local match_index = math.floor( tonumber(new_args['match']) or 1 )
local nomatch = new_args['nomatch']
return str._match( s, pattern, start, match_index, plain_flag, nomatch )
end
--[[
pos
This function returns a single character from the target string at position pos.
Usage:
{{#invoke:String|pos|target_string|index_value}}
OR
{{#invoke:String|pos|target=target_string|pos=index_value}}
Parameters
target: The string to search
pos: The index for the character to return
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the target string. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
The first character has an index value of 1.
If one requests a negative value, this function will select a character by counting backwards
from the end of the string. In other words pos = -1 is the same as asking for the last character.
A requested value of zero, or a value greater than the length of the string returns an error.
]]
function str.pos( frame )
local new_args = str._getParameters( frame.args, {'target', 'pos'} )
local target_str = new_args['target'] or ''
local pos = tonumber( new_args['pos'] ) or 0
if pos == 0 or math.abs(pos) > mw.ustring.len( target_str ) then
return str._error( 'String index out of range' )
end
return mw.ustring.sub( target_str, pos, pos )
end
--[[
str_find
This function duplicates the behavior of {{str_find}}, including all of its quirks.
This is provided in order to support existing templates, but is NOT RECOMMENDED for
new code and templates. New code is recommended to use the "find" function instead.
Returns the first index in "source" that is a match to "target". Indexing is 1-based,
and the function returns -1 if the "target" string is not present in "source".
Important Note: If the "target" string is empty / missing, this function returns a
value of "1", which is generally unexpected behavior, and must be accounted for
separatetly.
]]
function str.str_find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target'} )
local source_str = new_args['source'] or ''
local target_str = new_args['target'] or ''
if target_str == '' then
return 1
end
local start = mw.ustring.find( source_str, target_str, 1, true )
if start == nil then
start = -1
end
return start
end
--[[
find
This function allows one to search for a target string or pattern within another
string.
Usage:
{{#invoke:String|find|source_str|target_string|start_index|plain_flag}}
OR
{{#invoke:String|find|source=source_str|target=target_str|start=start_index|plain=plain_flag}}
Parameters
source: The string to search
target: The string or pattern to find within source
start: The index within the source string to start the search, defaults to 1
plain: Boolean flag indicating that target should be understood as plain
text and not as a Lua style regular expression, defaults to true
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the parameter. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
This function returns the first index >= "start" where "target" can be found
within "source". Indices are 1-based. If "target" is not found, then this
function returns 0. If either "source" or "target" are missing / empty, this
function also returns 0.
This function should be safe for UTF-8 strings.
]]
function str.find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target', 'start', 'plain' } )
local source_str = new_args['source'] or ''
local pattern = new_args['target'] or ''
local start_pos = tonumber(new_args['start']) or 1
local plain = new_args['plain'] or true
if source_str == '' or pattern == '' then
return 0
end
plain = str._getBoolean( plain )
local start = mw.ustring.find( source_str, pattern, start_pos, plain )
if start == nil then
start = 0
end
return start
end
--[[
replace
This function allows one to replace a target string or pattern within another
string.
Usage:
{{#invoke:String|replace|source_str|pattern_string|replace_string|replacement_count|plain_flag}}
OR
{{#invoke:String|replace|source=source_string|pattern=pattern_string|replace=replace_string|
count=replacement_count|plain=plain_flag}}
Parameters
source: The string to search
pattern: The string or pattern to find within source
replace: The replacement text
count: The number of occurences to replace, defaults to all.
plain: Boolean flag indicating that pattern should be understood as plain
text and not as a Lua style regular expression, defaults to true
]]
function str.replace( frame )
local new_args = str._getParameters( frame.args, {'source', 'pattern', 'replace', 'count', 'plain' } )
local source_str = new_args['source'] or ''
local pattern = new_args['pattern'] or ''
local replace = new_args['replace'] or ''
local count = tonumber( new_args['count'] )
local plain = new_args['plain'] or true
if source_str == '' or pattern == '' then
return source_str
end
plain = str._getBoolean( plain )
if plain then
pattern = str._escapePattern( pattern )
replace = string.gsub( replace, "%%", "%%%%" ) --Only need to escape replacement sequences.
end
local result
if count ~= nil then
result = mw.ustring.gsub( source_str, pattern, replace, count )
else
result = mw.ustring.gsub( source_str, pattern, replace )
end
return result
end
--[[
simple function to pipe string.rep to templates.
]]
function str.rep( frame )
local repetitions = tonumber( frame.args[2] )
if not repetitions then
return str._error( 'function rep expects a number as second parameter, received "' .. ( frame.args[2] or '' ) .. '"' )
end
return string.rep( frame.args[1] or '', repetitions )
end
--[[
escapePattern
This function escapes special characters from a Lua string pattern. See [1]
for details on how patterns work.
[1] https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
Usage:
{{#invoke:String|escapePattern|pattern_string}}
Parameters
pattern_string: The pattern string to escape.
]]
function str.escapePattern( frame )
local pattern_str = frame.args[1]
if not pattern_str then
return str._error( 'No pattern string specified' )
end
local result = str._escapePattern( pattern_str )
return result
end
--[[
count
This function counts the number of occurrences of one string in another.
]]
function str.count(frame)
local args = str._getParameters(frame.args, {'source', 'pattern', 'plain'})
local source = args.source or ''
local pattern = args.pattern or ''
local plain = str._getBoolean(args.plain or true)
if plain then
pattern = str._escapePattern(pattern)
end
local _, count = mw.ustring.gsub(source, pattern, '')
return count
end
--[[
endswith
This function determines whether a string ends with another string.
]]
function str.endswith(frame)
local args = str._getParameters(frame.args, {'source', 'pattern'})
local source = args.source or ''
local pattern = args.pattern or ''
if pattern == '' then
-- All strings end with the empty string.
return "yes"
end
if mw.ustring.sub(source, -mw.ustring.len(pattern), -1) == pattern then
return "yes"
else
return ""
end
end
--[[
join
Join all non empty arguments together; the first argument is the separator.
Usage:
{{#invoke:String|join|sep|one|two|three}}
]]
function str.join(frame)
local args = {}
local sep
for _, v in ipairs( frame.args ) do
if sep then
if v ~= '' then
table.insert(args, v)
end
else
sep = v
end
end
return table.concat( args, sep or '' )
end
-- findpagetext returns the position of a piece of text in a page
-- First positional parameter or |text is the search text
-- Optional parameter |title is the page title, defaults to current page
-- Optional parameter |plain is either true for plain search (default) or false for Lua pattern search
-- Optional parameter |nomatch is the return value when no match is found; default is nil
function str._findpagetext(args)
-- process parameters
local nomatch = args.nomatch or ""
if nomatch == "" then nomatch = nil end
--
local text = mw.text.trim(args[1] or args.text or "")
if text == "" then return nil end
--
local title = args.title or ""
local titleobj
if title == "" then
titleobj = mw.title.getCurrentTitle()
else
titleobj = mw.title.new(title)
end
--
local plain = args.plain or ""
if plain:sub(1, 1) == "f" then plain = false else plain = true end
-- get the page content and look for 'text' - return position or nomatch
local content = titleobj and titleobj:getContent()
return content and mw.ustring.find(content, text, 1, plain) or nomatch
end
function str.findpagetext(frame)
local args = frame.args
local pargs = frame:getParent().args
for k, v in pairs(pargs) do
args[k] = v
end
if not (args[1] or args.text) then return nil end
-- just the first value
return (p._findpagetext(args))
end
--[[
Helper function that populates the argument list given that user may need to use a mix of
named and unnamed parameters. This is relevant because named parameters are not
identical to unnamed parameters due to string trimming, and when dealing with strings
we sometimes want to either preserve or remove that whitespace depending on the application.
]]
function str._getParameters( frame_args, arg_list )
local new_args = {}
local index = 1
local value
for _, arg in ipairs( arg_list ) do
value = frame_args[arg]
if value == nil then
value = frame_args[index]
index = index + 1
end
new_args[arg] = value
end
return new_args
end
--[[
Helper function to handle error messages.
]]
function str._error( error_str )
local frame = mw.getCurrentFrame()
local error_category = frame.args.error_category or 'Errors reported by Module String'
local ignore_errors = frame.args.ignore_errors or false
local no_category = frame.args.no_category or false
if str._getBoolean(ignore_errors) then
return ''
end
local error_str = '<strong class="error">String Module Error: ' .. error_str .. '</strong>'
if error_category ~= '' and not str._getBoolean( no_category ) then
error_str = '[[Category:' .. error_category .. ']]' .. error_str
end
return error_str
end
--[[
Helper Function to interpret boolean strings
]]
function str._getBoolean( boolean_str )
local boolean_value
if type( boolean_str ) == 'string' then
boolean_str = boolean_str:lower()
if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0'
or boolean_str == '' then
boolean_value = false
else
boolean_value = true
end
elseif type( boolean_str ) == 'boolean' then
boolean_value = boolean_str
else
error( 'No boolean value found' )
end
return boolean_value
end
--[[
Helper function that escapes all pattern characters so that they will be treated
as plain text.
]]
function str._escapePattern( pattern_str )
return ( string.gsub( pattern_str, "[%(%)%.%%%+%-%*%?%[%^%$%]]", "%%%0" ) )
end
return str
eypp2ycor2x5h347mkrbxut7kqyr5ai
803565
803564
2025-08-11T23:48:56Z
en>Pppery
0
Fix
803565
Scribunto
text/plain
--[[
This module is intended to provide access to basic string functions.
Most of the functions provided here can be invoked with named parameters,
unnamed parameters, or a mixture. If named parameters are used, Mediawiki will
automatically remove any leading or trailing whitespace from the parameter.
Depending on the intended use, it may be advantageous to either preserve or
remove such whitespace.
Global options
ignore_errors: If set to 'true' or 1, any error condition will result in
an empty string being returned rather than an error message.
error_category: If an error occurs, specifies the name of a category to
include with the error message. The default category is
[Category:Errors reported by Module String].
no_category: If set to 'true' or 1, no category will be added if an error
is generated.
Unit tests for this module are available at Module:String/tests.
]]
local str = {}
--[[
len
This function returns the length of the target string.
Usage:
{{#invoke:String|len|target_string|}}
OR
{{#invoke:String|len|s=target_string}}
Parameters
s: The string whose length to report
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the target string.
]]
function str.len( frame )
local new_args = str._getParameters( frame.args, {'s'} )
local s = new_args['s'] or ''
return mw.ustring.len( s )
end
--[[
sub
This function returns a substring of the target string at specified indices.
Usage:
{{#invoke:String|sub|target_string|start_index|end_index}}
OR
{{#invoke:String|sub|s=target_string|i=start_index|j=end_index}}
Parameters
s: The string to return a subset of
i: The first index of the substring to return, defaults to 1.
j: The last index of the string to return, defaults to the last character.
The first character of the string is assigned an index of 1. If either i or j
is a negative value, it is interpreted the same as selecting a character by
counting from the end of the string. Hence, a value of -1 is the same as
selecting the last character of the string.
If the requested indices are out of range for the given string, an error is
reported.
]]
function str.sub( frame )
local new_args = str._getParameters( frame.args, { 's', 'i', 'j' } )
local s = new_args['s'] or ''
local i = tonumber( new_args['i'] ) or 1
local j = tonumber( new_args['j'] ) or -1
local len = mw.ustring.len( s )
-- Convert negatives for range checking
if i < 0 then
i = len + i + 1
end
if j < 0 then
j = len + j + 1
end
if i > len or j > len or i < 1 or j < 1 then
return str._error( 'String subset index out of range' )
end
if j < i then
return str._error( 'String subset indices out of order' )
end
return mw.ustring.sub( s, i, j )
end
--[[
This function implements that features of {{str sub old}} and is kept in order
to maintain these older templates.
]]
function str.sublength( frame )
local i = tonumber( frame.args.i ) or 0
local len = tonumber( frame.args.len )
return mw.ustring.sub( frame.args.s, i + 1, len and ( i + len ) )
end
--[[
_match
This function returns a substring from the source string that matches a
specified pattern. It is exported for use in other modules
Usage:
strmatch = require("Module:String")._match
sresult = strmatch( s, pattern, start, match, plain, nomatch )
Parameters
s: The string to search
pattern: The pattern or string to find within the string
start: The index within the source string to start the search. The first
character of the string has index 1. Defaults to 1.
match: In some cases it may be possible to make multiple matches on a single
string. This specifies which match to return, where the first match is
match= 1. If a negative number is specified then a match is returned
counting from the last match. Hence match = -1 is the same as requesting
the last match. Defaults to 1.
plain: A flag indicating that the pattern should be understood as plain
text. Defaults to false.
nomatch: If no match is found, output the "nomatch" value rather than an error.
For information on constructing Lua patterns, a form of [regular expression], see:
* http://www.lua.org/manual/5.1/manual.html#5.4.1
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns
]]
-- This sub-routine is exported for use in other modules
function str._match( s, pattern, start, match_index, plain_flag, nomatch )
if s == '' then
return str._error( 'Target string is empty' )
end
if pattern == '' then
return str._error( 'Pattern string is empty' )
end
start = tonumber(start) or 1
if math.abs(start) < 1 or math.abs(start) > mw.ustring.len( s ) then
return str._error( 'Requested start is out of range' )
end
if match_index == 0 then
return str._error( 'Match index is out of range' )
end
if plain_flag then
pattern = str._escapePattern( pattern )
end
local result
if match_index == 1 then
-- Find first match is simple case
result = mw.ustring.match( s, pattern, start )
else
if start > 1 then
s = mw.ustring.sub( s, start )
end
local iterator = mw.ustring.gmatch(s, pattern)
if match_index > 0 then
-- Forward search
for w in iterator do
match_index = match_index - 1
if match_index == 0 then
result = w
break
end
end
else
-- Reverse search
local result_table = {}
local count = 1
for w in iterator do
result_table[count] = w
count = count + 1
end
result = result_table[ count + match_index ]
end
end
if result == nil then
if nomatch == nil then
return str._error( 'Match not found' )
else
return nomatch
end
else
return result
end
end
--[[
match
This function returns a substring from the source string that matches a
specified pattern.
Usage:
{{#invoke:String|match|source_string|pattern_string|start_index|match_number|plain_flag|nomatch_output}}
OR
{{#invoke:String|match|s=source_string|pattern=pattern_string|start=start_index
|match=match_number|plain=plain_flag|nomatch=nomatch_output}}
Parameters
s: The string to search
pattern: The pattern or string to find within the string
start: The index within the source string to start the search. The first
character of the string has index 1. Defaults to 1.
match: In some cases it may be possible to make multiple matches on a single
string. This specifies which match to return, where the first match is
match= 1. If a negative number is specified then a match is returned
counting from the last match. Hence match = -1 is the same as requesting
the last match. Defaults to 1.
plain: A flag indicating that the pattern should be understood as plain
text. Defaults to false.
nomatch: If no match is found, output the "nomatch" value rather than an error.
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from each string. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
If the match_number or start_index are out of range for the string being queried, then
this function generates an error. An error is also generated if no match is found.
If one adds the parameter ignore_errors=true, then the error will be suppressed and
an empty string will be returned on any failure.
For information on constructing Lua patterns, a form of [regular expression], see:
* http://www.lua.org/manual/5.1/manual.html#5.4.1
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns
]]
-- This is the entry point for #invoke:String|match
function str.match( frame )
local new_args = str._getParameters( frame.args, {'s', 'pattern', 'start', 'match', 'plain', 'nomatch'} )
local s = new_args['s'] or ''
local start = tonumber( new_args['start'] ) or 1
local plain_flag = str._getBoolean( new_args['plain'] or false )
local pattern = new_args['pattern'] or ''
local match_index = math.floor( tonumber(new_args['match']) or 1 )
local nomatch = new_args['nomatch']
return str._match( s, pattern, start, match_index, plain_flag, nomatch )
end
--[[
pos
This function returns a single character from the target string at position pos.
Usage:
{{#invoke:String|pos|target_string|index_value}}
OR
{{#invoke:String|pos|target=target_string|pos=index_value}}
Parameters
target: The string to search
pos: The index for the character to return
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the target string. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
The first character has an index value of 1.
If one requests a negative value, this function will select a character by counting backwards
from the end of the string. In other words pos = -1 is the same as asking for the last character.
A requested value of zero, or a value greater than the length of the string returns an error.
]]
function str.pos( frame )
local new_args = str._getParameters( frame.args, {'target', 'pos'} )
local target_str = new_args['target'] or ''
local pos = tonumber( new_args['pos'] ) or 0
if pos == 0 or math.abs(pos) > mw.ustring.len( target_str ) then
return str._error( 'String index out of range' )
end
return mw.ustring.sub( target_str, pos, pos )
end
--[[
str_find
This function duplicates the behavior of {{str_find}}, including all of its quirks.
This is provided in order to support existing templates, but is NOT RECOMMENDED for
new code and templates. New code is recommended to use the "find" function instead.
Returns the first index in "source" that is a match to "target". Indexing is 1-based,
and the function returns -1 if the "target" string is not present in "source".
Important Note: If the "target" string is empty / missing, this function returns a
value of "1", which is generally unexpected behavior, and must be accounted for
separatetly.
]]
function str.str_find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target'} )
local source_str = new_args['source'] or ''
local target_str = new_args['target'] or ''
if target_str == '' then
return 1
end
local start = mw.ustring.find( source_str, target_str, 1, true )
if start == nil then
start = -1
end
return start
end
--[[
find
This function allows one to search for a target string or pattern within another
string.
Usage:
{{#invoke:String|find|source_str|target_string|start_index|plain_flag}}
OR
{{#invoke:String|find|source=source_str|target=target_str|start=start_index|plain=plain_flag}}
Parameters
source: The string to search
target: The string or pattern to find within source
start: The index within the source string to start the search, defaults to 1
plain: Boolean flag indicating that target should be understood as plain
text and not as a Lua style regular expression, defaults to true
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the parameter. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
This function returns the first index >= "start" where "target" can be found
within "source". Indices are 1-based. If "target" is not found, then this
function returns 0. If either "source" or "target" are missing / empty, this
function also returns 0.
This function should be safe for UTF-8 strings.
]]
function str.find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target', 'start', 'plain' } )
local source_str = new_args['source'] or ''
local pattern = new_args['target'] or ''
local start_pos = tonumber(new_args['start']) or 1
local plain = new_args['plain'] or true
if source_str == '' or pattern == '' then
return 0
end
plain = str._getBoolean( plain )
local start = mw.ustring.find( source_str, pattern, start_pos, plain )
if start == nil then
start = 0
end
return start
end
--[[
replace
This function allows one to replace a target string or pattern within another
string.
Usage:
{{#invoke:String|replace|source_str|pattern_string|replace_string|replacement_count|plain_flag}}
OR
{{#invoke:String|replace|source=source_string|pattern=pattern_string|replace=replace_string|
count=replacement_count|plain=plain_flag}}
Parameters
source: The string to search
pattern: The string or pattern to find within source
replace: The replacement text
count: The number of occurences to replace, defaults to all.
plain: Boolean flag indicating that pattern should be understood as plain
text and not as a Lua style regular expression, defaults to true
]]
function str.replace( frame )
local new_args = str._getParameters( frame.args, {'source', 'pattern', 'replace', 'count', 'plain' } )
local source_str = new_args['source'] or ''
local pattern = new_args['pattern'] or ''
local replace = new_args['replace'] or ''
local count = tonumber( new_args['count'] )
local plain = new_args['plain'] or true
if source_str == '' or pattern == '' then
return source_str
end
plain = str._getBoolean( plain )
if plain then
pattern = str._escapePattern( pattern )
replace = string.gsub( replace, "%%", "%%%%" ) --Only need to escape replacement sequences.
end
local result
if count ~= nil then
result = mw.ustring.gsub( source_str, pattern, replace, count )
else
result = mw.ustring.gsub( source_str, pattern, replace )
end
return result
end
--[[
simple function to pipe string.rep to templates.
]]
function str.rep( frame )
local repetitions = tonumber( frame.args[2] )
if not repetitions then
return str._error( 'function rep expects a number as second parameter, received "' .. ( frame.args[2] or '' ) .. '"' )
end
return string.rep( frame.args[1] or '', repetitions )
end
--[[
escapePattern
This function escapes special characters from a Lua string pattern. See [1]
for details on how patterns work.
[1] https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
Usage:
{{#invoke:String|escapePattern|pattern_string}}
Parameters
pattern_string: The pattern string to escape.
]]
function str.escapePattern( frame )
local pattern_str = frame.args[1]
if not pattern_str then
return str._error( 'No pattern string specified' )
end
local result = str._escapePattern( pattern_str )
return result
end
--[[
count
This function counts the number of occurrences of one string in another.
]]
function str.count(frame)
local args = str._getParameters(frame.args, {'source', 'pattern', 'plain'})
local source = args.source or ''
local pattern = args.pattern or ''
local plain = str._getBoolean(args.plain or true)
if plain then
pattern = str._escapePattern(pattern)
end
local _, count = mw.ustring.gsub(source, pattern, '')
return count
end
--[[
endswith
This function determines whether a string ends with another string.
]]
function str.endswith(frame)
local args = str._getParameters(frame.args, {'source', 'pattern'})
local source = args.source or ''
local pattern = args.pattern or ''
if pattern == '' then
-- All strings end with the empty string.
return "yes"
end
if mw.ustring.sub(source, -mw.ustring.len(pattern), -1) == pattern then
return "yes"
else
return ""
end
end
--[[
join
Join all non empty arguments together; the first argument is the separator.
Usage:
{{#invoke:String|join|sep|one|two|three}}
]]
function str.join(frame)
local args = {}
local sep
for _, v in ipairs( frame.args ) do
if sep then
if v ~= '' then
table.insert(args, v)
end
else
sep = v
end
end
return table.concat( args, sep or '' )
end
-- findpagetext returns the position of a piece of text in a page
-- First positional parameter or |text is the search text
-- Optional parameter |title is the page title, defaults to current page
-- Optional parameter |plain is either true for plain search (default) or false for Lua pattern search
-- Optional parameter |nomatch is the return value when no match is found; default is nil
function str._findpagetext(args)
-- process parameters
local nomatch = args.nomatch or ""
if nomatch == "" then nomatch = nil end
--
local text = mw.text.trim(args[1] or args.text or "")
if text == "" then return nil end
--
local title = args.title or ""
local titleobj
if title == "" then
titleobj = mw.title.getCurrentTitle()
else
titleobj = mw.title.new(title)
end
--
local plain = args.plain or ""
if plain:sub(1, 1) == "f" then plain = false else plain = true end
-- get the page content and look for 'text' - return position or nomatch
local content = titleobj and titleobj:getContent()
return content and mw.ustring.find(content, text, 1, plain) or nomatch
end
function str.findpagetext(frame)
local args = frame.args
local pargs = frame:getParent().args
for k, v in pairs(pargs) do
args[k] = v
end
if not (args[1] or args.text) then return nil end
-- just the first value
return (str._findpagetext(args))
end
--[[
Helper function that populates the argument list given that user may need to use a mix of
named and unnamed parameters. This is relevant because named parameters are not
identical to unnamed parameters due to string trimming, and when dealing with strings
we sometimes want to either preserve or remove that whitespace depending on the application.
]]
function str._getParameters( frame_args, arg_list )
local new_args = {}
local index = 1
local value
for _, arg in ipairs( arg_list ) do
value = frame_args[arg]
if value == nil then
value = frame_args[index]
index = index + 1
end
new_args[arg] = value
end
return new_args
end
--[[
Helper function to handle error messages.
]]
function str._error( error_str )
local frame = mw.getCurrentFrame()
local error_category = frame.args.error_category or 'Errors reported by Module String'
local ignore_errors = frame.args.ignore_errors or false
local no_category = frame.args.no_category or false
if str._getBoolean(ignore_errors) then
return ''
end
local error_str = '<strong class="error">String Module Error: ' .. error_str .. '</strong>'
if error_category ~= '' and not str._getBoolean( no_category ) then
error_str = '[[Category:' .. error_category .. ']]' .. error_str
end
return error_str
end
--[[
Helper Function to interpret boolean strings
]]
function str._getBoolean( boolean_str )
local boolean_value
if type( boolean_str ) == 'string' then
boolean_str = boolean_str:lower()
if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0'
or boolean_str == '' then
boolean_value = false
else
boolean_value = true
end
elseif type( boolean_str ) == 'boolean' then
boolean_value = boolean_str
else
error( 'No boolean value found' )
end
return boolean_value
end
--[[
Helper function that escapes all pattern characters so that they will be treated
as plain text.
]]
function str._escapePattern( pattern_str )
return ( string.gsub( pattern_str, "[%(%)%.%%%+%-%*%?%[%^%$%]]", "%%%0" ) )
end
return str
j3b8d4sejqnvazpfl0gk0yfaa503gli
803566
803565
2026-07-30T00:17:01Z
SM7
3953
2 revisions imported from [[:en:Module:String]]
803565
Scribunto
text/plain
--[[
This module is intended to provide access to basic string functions.
Most of the functions provided here can be invoked with named parameters,
unnamed parameters, or a mixture. If named parameters are used, Mediawiki will
automatically remove any leading or trailing whitespace from the parameter.
Depending on the intended use, it may be advantageous to either preserve or
remove such whitespace.
Global options
ignore_errors: If set to 'true' or 1, any error condition will result in
an empty string being returned rather than an error message.
error_category: If an error occurs, specifies the name of a category to
include with the error message. The default category is
[Category:Errors reported by Module String].
no_category: If set to 'true' or 1, no category will be added if an error
is generated.
Unit tests for this module are available at Module:String/tests.
]]
local str = {}
--[[
len
This function returns the length of the target string.
Usage:
{{#invoke:String|len|target_string|}}
OR
{{#invoke:String|len|s=target_string}}
Parameters
s: The string whose length to report
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the target string.
]]
function str.len( frame )
local new_args = str._getParameters( frame.args, {'s'} )
local s = new_args['s'] or ''
return mw.ustring.len( s )
end
--[[
sub
This function returns a substring of the target string at specified indices.
Usage:
{{#invoke:String|sub|target_string|start_index|end_index}}
OR
{{#invoke:String|sub|s=target_string|i=start_index|j=end_index}}
Parameters
s: The string to return a subset of
i: The first index of the substring to return, defaults to 1.
j: The last index of the string to return, defaults to the last character.
The first character of the string is assigned an index of 1. If either i or j
is a negative value, it is interpreted the same as selecting a character by
counting from the end of the string. Hence, a value of -1 is the same as
selecting the last character of the string.
If the requested indices are out of range for the given string, an error is
reported.
]]
function str.sub( frame )
local new_args = str._getParameters( frame.args, { 's', 'i', 'j' } )
local s = new_args['s'] or ''
local i = tonumber( new_args['i'] ) or 1
local j = tonumber( new_args['j'] ) or -1
local len = mw.ustring.len( s )
-- Convert negatives for range checking
if i < 0 then
i = len + i + 1
end
if j < 0 then
j = len + j + 1
end
if i > len or j > len or i < 1 or j < 1 then
return str._error( 'String subset index out of range' )
end
if j < i then
return str._error( 'String subset indices out of order' )
end
return mw.ustring.sub( s, i, j )
end
--[[
This function implements that features of {{str sub old}} and is kept in order
to maintain these older templates.
]]
function str.sublength( frame )
local i = tonumber( frame.args.i ) or 0
local len = tonumber( frame.args.len )
return mw.ustring.sub( frame.args.s, i + 1, len and ( i + len ) )
end
--[[
_match
This function returns a substring from the source string that matches a
specified pattern. It is exported for use in other modules
Usage:
strmatch = require("Module:String")._match
sresult = strmatch( s, pattern, start, match, plain, nomatch )
Parameters
s: The string to search
pattern: The pattern or string to find within the string
start: The index within the source string to start the search. The first
character of the string has index 1. Defaults to 1.
match: In some cases it may be possible to make multiple matches on a single
string. This specifies which match to return, where the first match is
match= 1. If a negative number is specified then a match is returned
counting from the last match. Hence match = -1 is the same as requesting
the last match. Defaults to 1.
plain: A flag indicating that the pattern should be understood as plain
text. Defaults to false.
nomatch: If no match is found, output the "nomatch" value rather than an error.
For information on constructing Lua patterns, a form of [regular expression], see:
* http://www.lua.org/manual/5.1/manual.html#5.4.1
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns
]]
-- This sub-routine is exported for use in other modules
function str._match( s, pattern, start, match_index, plain_flag, nomatch )
if s == '' then
return str._error( 'Target string is empty' )
end
if pattern == '' then
return str._error( 'Pattern string is empty' )
end
start = tonumber(start) or 1
if math.abs(start) < 1 or math.abs(start) > mw.ustring.len( s ) then
return str._error( 'Requested start is out of range' )
end
if match_index == 0 then
return str._error( 'Match index is out of range' )
end
if plain_flag then
pattern = str._escapePattern( pattern )
end
local result
if match_index == 1 then
-- Find first match is simple case
result = mw.ustring.match( s, pattern, start )
else
if start > 1 then
s = mw.ustring.sub( s, start )
end
local iterator = mw.ustring.gmatch(s, pattern)
if match_index > 0 then
-- Forward search
for w in iterator do
match_index = match_index - 1
if match_index == 0 then
result = w
break
end
end
else
-- Reverse search
local result_table = {}
local count = 1
for w in iterator do
result_table[count] = w
count = count + 1
end
result = result_table[ count + match_index ]
end
end
if result == nil then
if nomatch == nil then
return str._error( 'Match not found' )
else
return nomatch
end
else
return result
end
end
--[[
match
This function returns a substring from the source string that matches a
specified pattern.
Usage:
{{#invoke:String|match|source_string|pattern_string|start_index|match_number|plain_flag|nomatch_output}}
OR
{{#invoke:String|match|s=source_string|pattern=pattern_string|start=start_index
|match=match_number|plain=plain_flag|nomatch=nomatch_output}}
Parameters
s: The string to search
pattern: The pattern or string to find within the string
start: The index within the source string to start the search. The first
character of the string has index 1. Defaults to 1.
match: In some cases it may be possible to make multiple matches on a single
string. This specifies which match to return, where the first match is
match= 1. If a negative number is specified then a match is returned
counting from the last match. Hence match = -1 is the same as requesting
the last match. Defaults to 1.
plain: A flag indicating that the pattern should be understood as plain
text. Defaults to false.
nomatch: If no match is found, output the "nomatch" value rather than an error.
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from each string. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
If the match_number or start_index are out of range for the string being queried, then
this function generates an error. An error is also generated if no match is found.
If one adds the parameter ignore_errors=true, then the error will be suppressed and
an empty string will be returned on any failure.
For information on constructing Lua patterns, a form of [regular expression], see:
* http://www.lua.org/manual/5.1/manual.html#5.4.1
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns
]]
-- This is the entry point for #invoke:String|match
function str.match( frame )
local new_args = str._getParameters( frame.args, {'s', 'pattern', 'start', 'match', 'plain', 'nomatch'} )
local s = new_args['s'] or ''
local start = tonumber( new_args['start'] ) or 1
local plain_flag = str._getBoolean( new_args['plain'] or false )
local pattern = new_args['pattern'] or ''
local match_index = math.floor( tonumber(new_args['match']) or 1 )
local nomatch = new_args['nomatch']
return str._match( s, pattern, start, match_index, plain_flag, nomatch )
end
--[[
pos
This function returns a single character from the target string at position pos.
Usage:
{{#invoke:String|pos|target_string|index_value}}
OR
{{#invoke:String|pos|target=target_string|pos=index_value}}
Parameters
target: The string to search
pos: The index for the character to return
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the target string. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
The first character has an index value of 1.
If one requests a negative value, this function will select a character by counting backwards
from the end of the string. In other words pos = -1 is the same as asking for the last character.
A requested value of zero, or a value greater than the length of the string returns an error.
]]
function str.pos( frame )
local new_args = str._getParameters( frame.args, {'target', 'pos'} )
local target_str = new_args['target'] or ''
local pos = tonumber( new_args['pos'] ) or 0
if pos == 0 or math.abs(pos) > mw.ustring.len( target_str ) then
return str._error( 'String index out of range' )
end
return mw.ustring.sub( target_str, pos, pos )
end
--[[
str_find
This function duplicates the behavior of {{str_find}}, including all of its quirks.
This is provided in order to support existing templates, but is NOT RECOMMENDED for
new code and templates. New code is recommended to use the "find" function instead.
Returns the first index in "source" that is a match to "target". Indexing is 1-based,
and the function returns -1 if the "target" string is not present in "source".
Important Note: If the "target" string is empty / missing, this function returns a
value of "1", which is generally unexpected behavior, and must be accounted for
separatetly.
]]
function str.str_find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target'} )
local source_str = new_args['source'] or ''
local target_str = new_args['target'] or ''
if target_str == '' then
return 1
end
local start = mw.ustring.find( source_str, target_str, 1, true )
if start == nil then
start = -1
end
return start
end
--[[
find
This function allows one to search for a target string or pattern within another
string.
Usage:
{{#invoke:String|find|source_str|target_string|start_index|plain_flag}}
OR
{{#invoke:String|find|source=source_str|target=target_str|start=start_index|plain=plain_flag}}
Parameters
source: The string to search
target: The string or pattern to find within source
start: The index within the source string to start the search, defaults to 1
plain: Boolean flag indicating that target should be understood as plain
text and not as a Lua style regular expression, defaults to true
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the parameter. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
This function returns the first index >= "start" where "target" can be found
within "source". Indices are 1-based. If "target" is not found, then this
function returns 0. If either "source" or "target" are missing / empty, this
function also returns 0.
This function should be safe for UTF-8 strings.
]]
function str.find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target', 'start', 'plain' } )
local source_str = new_args['source'] or ''
local pattern = new_args['target'] or ''
local start_pos = tonumber(new_args['start']) or 1
local plain = new_args['plain'] or true
if source_str == '' or pattern == '' then
return 0
end
plain = str._getBoolean( plain )
local start = mw.ustring.find( source_str, pattern, start_pos, plain )
if start == nil then
start = 0
end
return start
end
--[[
replace
This function allows one to replace a target string or pattern within another
string.
Usage:
{{#invoke:String|replace|source_str|pattern_string|replace_string|replacement_count|plain_flag}}
OR
{{#invoke:String|replace|source=source_string|pattern=pattern_string|replace=replace_string|
count=replacement_count|plain=plain_flag}}
Parameters
source: The string to search
pattern: The string or pattern to find within source
replace: The replacement text
count: The number of occurences to replace, defaults to all.
plain: Boolean flag indicating that pattern should be understood as plain
text and not as a Lua style regular expression, defaults to true
]]
function str.replace( frame )
local new_args = str._getParameters( frame.args, {'source', 'pattern', 'replace', 'count', 'plain' } )
local source_str = new_args['source'] or ''
local pattern = new_args['pattern'] or ''
local replace = new_args['replace'] or ''
local count = tonumber( new_args['count'] )
local plain = new_args['plain'] or true
if source_str == '' or pattern == '' then
return source_str
end
plain = str._getBoolean( plain )
if plain then
pattern = str._escapePattern( pattern )
replace = string.gsub( replace, "%%", "%%%%" ) --Only need to escape replacement sequences.
end
local result
if count ~= nil then
result = mw.ustring.gsub( source_str, pattern, replace, count )
else
result = mw.ustring.gsub( source_str, pattern, replace )
end
return result
end
--[[
simple function to pipe string.rep to templates.
]]
function str.rep( frame )
local repetitions = tonumber( frame.args[2] )
if not repetitions then
return str._error( 'function rep expects a number as second parameter, received "' .. ( frame.args[2] or '' ) .. '"' )
end
return string.rep( frame.args[1] or '', repetitions )
end
--[[
escapePattern
This function escapes special characters from a Lua string pattern. See [1]
for details on how patterns work.
[1] https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
Usage:
{{#invoke:String|escapePattern|pattern_string}}
Parameters
pattern_string: The pattern string to escape.
]]
function str.escapePattern( frame )
local pattern_str = frame.args[1]
if not pattern_str then
return str._error( 'No pattern string specified' )
end
local result = str._escapePattern( pattern_str )
return result
end
--[[
count
This function counts the number of occurrences of one string in another.
]]
function str.count(frame)
local args = str._getParameters(frame.args, {'source', 'pattern', 'plain'})
local source = args.source or ''
local pattern = args.pattern or ''
local plain = str._getBoolean(args.plain or true)
if plain then
pattern = str._escapePattern(pattern)
end
local _, count = mw.ustring.gsub(source, pattern, '')
return count
end
--[[
endswith
This function determines whether a string ends with another string.
]]
function str.endswith(frame)
local args = str._getParameters(frame.args, {'source', 'pattern'})
local source = args.source or ''
local pattern = args.pattern or ''
if pattern == '' then
-- All strings end with the empty string.
return "yes"
end
if mw.ustring.sub(source, -mw.ustring.len(pattern), -1) == pattern then
return "yes"
else
return ""
end
end
--[[
join
Join all non empty arguments together; the first argument is the separator.
Usage:
{{#invoke:String|join|sep|one|two|three}}
]]
function str.join(frame)
local args = {}
local sep
for _, v in ipairs( frame.args ) do
if sep then
if v ~= '' then
table.insert(args, v)
end
else
sep = v
end
end
return table.concat( args, sep or '' )
end
-- findpagetext returns the position of a piece of text in a page
-- First positional parameter or |text is the search text
-- Optional parameter |title is the page title, defaults to current page
-- Optional parameter |plain is either true for plain search (default) or false for Lua pattern search
-- Optional parameter |nomatch is the return value when no match is found; default is nil
function str._findpagetext(args)
-- process parameters
local nomatch = args.nomatch or ""
if nomatch == "" then nomatch = nil end
--
local text = mw.text.trim(args[1] or args.text or "")
if text == "" then return nil end
--
local title = args.title or ""
local titleobj
if title == "" then
titleobj = mw.title.getCurrentTitle()
else
titleobj = mw.title.new(title)
end
--
local plain = args.plain or ""
if plain:sub(1, 1) == "f" then plain = false else plain = true end
-- get the page content and look for 'text' - return position or nomatch
local content = titleobj and titleobj:getContent()
return content and mw.ustring.find(content, text, 1, plain) or nomatch
end
function str.findpagetext(frame)
local args = frame.args
local pargs = frame:getParent().args
for k, v in pairs(pargs) do
args[k] = v
end
if not (args[1] or args.text) then return nil end
-- just the first value
return (str._findpagetext(args))
end
--[[
Helper function that populates the argument list given that user may need to use a mix of
named and unnamed parameters. This is relevant because named parameters are not
identical to unnamed parameters due to string trimming, and when dealing with strings
we sometimes want to either preserve or remove that whitespace depending on the application.
]]
function str._getParameters( frame_args, arg_list )
local new_args = {}
local index = 1
local value
for _, arg in ipairs( arg_list ) do
value = frame_args[arg]
if value == nil then
value = frame_args[index]
index = index + 1
end
new_args[arg] = value
end
return new_args
end
--[[
Helper function to handle error messages.
]]
function str._error( error_str )
local frame = mw.getCurrentFrame()
local error_category = frame.args.error_category or 'Errors reported by Module String'
local ignore_errors = frame.args.ignore_errors or false
local no_category = frame.args.no_category or false
if str._getBoolean(ignore_errors) then
return ''
end
local error_str = '<strong class="error">String Module Error: ' .. error_str .. '</strong>'
if error_category ~= '' and not str._getBoolean( no_category ) then
error_str = '[[Category:' .. error_category .. ']]' .. error_str
end
return error_str
end
--[[
Helper Function to interpret boolean strings
]]
function str._getBoolean( boolean_str )
local boolean_value
if type( boolean_str ) == 'string' then
boolean_str = boolean_str:lower()
if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0'
or boolean_str == '' then
boolean_value = false
else
boolean_value = true
end
elseif type( boolean_str ) == 'boolean' then
boolean_value = boolean_str
else
error( 'No boolean value found' )
end
return boolean_value
end
--[[
Helper function that escapes all pattern characters so that they will be treated
as plain text.
]]
function str._escapePattern( pattern_str )
return ( string.gsub( pattern_str, "[%(%)%.%%%+%-%*%?%[%^%$%]]", "%%%0" ) )
end
return str
j3b8d4sejqnvazpfl0gk0yfaa503gli
12 फरवरी
0
21682
803392
784501
2026-07-29T18:42:04Z
SM7
3953
Restored revision 738926 by [[Special:Contributions/SM7Bot|SM7Bot]] ([[User talk:SM7Bot|talk]]): Rv / vand
803392
wikitext
text/x-wiki
{{कैलेंडर}}
{{पिछला कुछ बरिस में ई दिन}}
{{दिन}}
==घटना==
==जनम==
* 1824 -दयानंद सरस्वती-एगो समाज सुधारक संत रहलन
*1920 - '''प्राण''' - हिंदी सलीमा क एगो अभिनेता रहलन
==निधन ==
==तिहुआर, छुट्टी अउरी खास महत्व ==
{{commons|February 12|12 फरवरी}}
{{साल के महीना सभ}}
[[श्रेणी:साल के दिन]]
[[श्रेणी:फरवरी]]
mnzttrzd2i2d5seuhdhb62eey7nhqq3
803394
803392
2026-07-29T18:46:07Z
SM7
3953
सुधार कइल गइल
803394
wikitext
text/x-wiki
{{कैलेंडर}}
{{पिछला कुछ बरिस में ई दिन}}
{{दिन}}
== घटना ==
== जनम ==
* [[1824]] — [[दयानंद सरस्वती]] — एगो समाज सुधारक संत रहलन
* [[1920]] — '''[[प्राण (एक्टर)|प्राण]]''' — हिंदी सलीमा क एगो अभिनेता रहलन
== निधन ==
== तिहुआर, छुट्टी अउरी खास महत्व ==
{{commons|February 12|12 फरवरी}}
{{साल के महीना सभ}}
[[श्रेणी:साल के दिन]]
[[श्रेणी:फरवरी]]
1yvd2qj3rpbn9zanymlvrmccknifngz
बर्की के लड़ाई
0
26192
803306
782048
2026-07-29T16:07:16Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803306
wikitext
text/x-wiki
{{Infobox military conflict
|conflict =बर्की के लड़ाई
|width =
|partof =[[1965 के भारत-पाक युद्ध]]
|image =
|caption =ले॰ कर्नल [[हरी सिंह (सिपाही)|हरी सिंह]], भारतीय सेना के [[Indian 1st Armoured Division|18वीं कैवेलरी]], भारत के सेना द्वारा लाहौर जिला के बर्की पुलिस स्टेशन के बाहर फोट खिंचावत।
|date =8-11 सितंबर 1965 के भइल<ref name="Major_1965">{{cite book
|last = सिंह
|first = ग्रुप कैप्टन रणबीर
|title = Major defence operations since 1947
|publisher = Ocean Books
|isbn = 81-88322-67-9}}</ref><ref name="Gupta_1965">{{cite book
|last = Gupta
|first = Hari Ram
|title = India-Pakistan war, 1965, Volume 1
|publisher = Hariyana Prakashan, 1967
|pages = 154–157
|isbn = }}</ref>
|place =[[बर्की, पाकिस्तान|बर्की]], [[लाहौर]], [[पाकिस्तान]]
|coordinates ={{coord |31|2|52|N|74|5|6|E|display=inline,title}}
|map_type =
|map_relief =
|latitude =
|longitude =
|map_size =
|map_marksize =
|map_caption =
|map_label =
|territory =
|result =भारत के निर्णायक बिजय<ref name="sainik">{{cite web |url= http://sainiksamachar.nic.in/englisharchives/2009/jan15-09/h61.html|title= Battle of Burki was another outstanding infantry operation|author= Melville de Mellow|date= 28 November 1965|work= |publisher= Sainik Samachar|accessdate=}}</ref><ref name="Singh_1965"/><br>
*लाहौर के ओर पाकिस्तानी टैंक हमला अउरी जवाबी हमला के असफलता <ref name="sainik">{{cite web |url= http://sainiksamachar.nic.in/englisharchives/2009/jan15-09/h61.html|title= Battle of Burki was another outstanding infantry operation|author= Melville de Mellow|date= 28 November 1965|work= |publisher= Sainik Samachar|accessdate=}}</ref><ref name="Singh_1965"/><ref>{{cite book
|last = Singh
|first = Gp Capt Ranbir
|title = Memorable War Stories
|publisher = Ocean Books
|isbn = }}</ref>
|status =
|combatants_header =
|combatant1 =<center>{{flagicon|India|size=65px}}<br>[[भारत]]</center>
|combatant2 =<center>{{flagicon|Pakistan|size=65px}}<br>[[Pakistan]]</center>
|commander1 ={{flagicon|India}} हर कृषण सिब्बल के बा<br>{{flagicon|India}} अनंत सिंह के ह
|commander2 ={{flagicon|Pakistan}} [[Raja Aziz Bhatti]] {{KIA}}
|units1 =
|units2 =
|strength1 =1 infantry division<ref name="Major_1965"/><br> 1 Armor Regiment<ref name="Chand_1965">{{cite book
|last = Das
|first = Chand N.
|title = Hours of Glory: Famous Battles of the Indian army, 1801–1971
|publisher = Vision Books, 1997
|isbn = }}</ref>
|strength2 =150 tanks<ref name="sainik"/>(3 Regiments)<br>2 Companies of 17 Punjab Regiment<br> 10 F-86 Sabre jets
|casualties1 =4 tanks<ref name="sainik"/>
|casualties2 =84 tanks<ref name="sainik"/>
|notes =
|campaignbox ={{Campaignbox Indo-Pakistani War of 1965}}<br>{{Campaignbox Indo-Pakistani Wars}}
}}
'''बर्की के लड़ाई''' भारत आ पाकिस्तान के सेना के बीच लड़ल गइल [[1965 के भारत-पाक युद्ध|1965 के युद्ध]]के हिस्सा रहल।<ref name="sainik"/> [[बर्की, पाकिस्तान|बर्की]] <small>({{Langx|ur|{{Nastaliq|'''بركى'''}}}})</small> पाकिस्तान में [[लाहौर]] के {{convert|6|mi|km|sp=us}} दक्खिन-पूरुब में भारत-पाकिस्तान बार्डर के नजदीक जगह बाटे<ref name="Ram_1965">{{cite book
|last = Gopal
|first = Ram
|title = Indo-Pakistan War and Peace, 1965
|publisher = Pustak Kendra, 1967
|page = 118
|isbn = }}</ref> आ ई लाहौर से [[इच्छोगिल नहर]] के पुल द्वारा जुड़ल बाटे। लड़ाई के समय दुनों देस के सेना के संख्या लगभग बराबर रहे। पाकिस्तानी सैनिक नहर के किनारे खाई खन के ओह में से लड़त रहलें आ टैंक आ फाइटर जेट के सपोर्ट भी रहे।<ref name="sainik"/> भारतीय सेना 11 सितंबर 1965 के एह जगह के जीत लिहलस।<ref>{{cite news|last1=फज़ल|first1=रेहान|title=बर्की की वो घमासान लड़ाई|url=http://www.bbc.com/hindi/multimedia/2015/09/150911_1965_war_part11_vr|accessdate=11 जनवरी 2016|publisher=बीबीसी हिंदी|date=11 सितंबर 2015|language=hi}}</ref>
==बैकग्राउंड==
पाकिस्तान 17 अगस्त 1965 के ऑपरेशन ग्रांड स्लैम शुरू कइलस, एह कोशिश में कि 15 अगस्त के ऑपरेशन जिब्राल्टर के असफलता के बाद घेराइल घुसपैठियन के राहत दिहल जा सके आ भारतीय आपूर्ति लाइन काट के कोसिस कइल गइल।<ref name="global">{{cite web |url= http://www.globalsecurity.org/military/world/war/indo-pak_1965.htm|title= Indo-Pakistan War of 1965|author= |work= |publisher= globalsecurity.org|accessdate=}}</ref> ऑपरेशन ग्रांड स्लैम के चलते आपूर्ति लाइन प बहुत तनाव के चलते भारत युद्ध में दूसरा मोर्चा खोले अउरी [[कश्मीर]] से पाकिस्तान के ध्यान भटकावे खाती लाहौर के ओर हमला शुरू कइलस।<ref name="global"/> लाहौर मोर्चा खोलला के बाद भारतीय सेना तीन गो कुल्हाड़ी-अमृतसर-लाहौर, खालरा-बुर्की-लाहौर आ खेम करण-कसूर सड़कन के साथ लाहौर के ओर बढ़ल आ छोट पाकिस्तानी सेना पर भारी पड़ल।<ref name="Johri_1965">{{cite book
|last = Johri
|first = Sitaram
|title = The Indo-Pak conflict of 1965
|publisher = Himalaya Publications, 1967
|pages = 129–130
|isbn = }}</ref>
एकमात्र भारतीय बख्तरबंद डिवीजन के समर्थन से भारतीय पैदल सेना जल्दी से अप्रस्तुत पाकिस्तानी रक्षकन के पीछे धकेल दिहलस जवना के मकसद लाहौर के घेरे आ संभवतः घेराबंदी कइल रहे| आश्चर्य के तत्व के चलते भारत भारत के सीमावर्ती शहर खलरा शहर से पाकिस्तानी इलाका के भारी मात्रा प कब्जा करे में कामयाब भईल जवन कि बुर्की से होके लाहौर जाए वाला सीधा सड़क प पड़ेला।<ref name="Gupta_1965"/><ref name="sainik"/> एह बीच पाकिस्तानी सेना एह इलाका में सैनिकन के जुटा लिहलसि आ तीन गो जवाबी हमला क के खोवल जमीन के फेर से कब्जा कर लिहलसि. एकरा बाद बुर्की के लड़ाई खलरा-बुर्की- लाहौर सड़क पर भइल।<ref name="sainik"/><ref name="Johri_1965"/>
पाकिस्तान के मुख्य लक्ष्य रहे कि भारतीय पैदल सेना के बख्तरबंद सपोर्ट अउरी सप्लाई लाइन के पकड़े से पहिले पीछे हटे खातिर मजबूर कइल जाव| पाकिस्तानी सेना के मकसद भी रहे कि उ लड़ाई में पहिले जवन इलाका गंवा चुकल रहे ओकर बहुत हिस्सा प कब्जा कइल जाए|<ref name="Gupta_1965"/> भारतीय पैदल सेना के मकसद रहे कि जब तक कवच आ आपूर्ति समेत सुदृढीकरण ना आ जाव तबले बुर्की शहर पर कब्जा कर के ओकरा के पकड़ल जाव।<ref name="Major_1965"/><ref name="Gupta_1965"/>
==संदर्भ==
{{reflist}}
<!--
{{Military of India}}
{{Military of Pakistan}}
-->
{{भारत-इति-आधार}}
{{पाकिस्तान-आधार}}
[[श्रेणी:भारत–पाकिस्तान संबंध]]
[[श्रेणी:पाकिस्तान के इतिहास]]
[[श्रेणी:भारत के इतिहास]]
bn0g3nmjg2chrl2olhovz5bk476cv7t
Module:Infobox
828
27376
803415
298492
2017-02-16T04:05:01Z
en>Mr. Stradivarius
0
Changed protection level for "[[Module:Infobox]]": [[WP:High-risk templates|High-risk Lua module]]: allow template editors ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
298492
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:wikitext(rowArgs.header)
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:newline()
:wikitext(rowArgs.data)
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(args.above)
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(args.below)
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
if args.child == 'yes' and args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass('infobox')
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
6706hcou5akn7nrr69xksoq3fnha2yw
803416
803415
2017-04-04T05:39:21Z
en>Frietjes
0
rowcellstyle per talk page
803416
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.header)
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:newline()
:wikitext(rowArgs.data)
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(args.above)
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(args.below)
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
if args.child == 'yes' and args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass('infobox')
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
edprki3cbc91qzciqck4g7yuj9k0n18
803417
803416
2017-05-29T13:38:50Z
en>Jc86035
0
fixes for child infoboxes so HTML Tidy is not required
803417
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local orphancat = 'Category:Orphan infoboxes'
local function notempty( s ) return s and s:match( '%S' ) end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local s = {}
local ttype = 'td'
if rowArgs.header then
rowArgs.data = nil
s = mw.text.split(rowArgs.header, '%[%[' .. orphancat .. '%]%]')
if notempty(s[1]) then
rowArgs.header = s[1]
else
rowArgs.header = nil
end
ttype = 'th'
elseif rowArgs.data then
s = mw.text.split(rowArgs.data, '%[%[' .. orphancat .. '%]%]')
if notempty(s[1]) then
rowArgs.data = s[1]
else
rowArgs.data = nil
end
ttype = 'td'
end
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.header)
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local s = mw.text.split(rowArgs.data, '%[%[' .. orphancat .. '%]%]')
rowArgs.data = s[1]
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:newline()
:wikitext(rowArgs.data)
end
for i = 2, #s do
if notempty(s[i]) then
if (i % 2) == 0 then
root
:wikitext(s[i])
else
local dataCell = root:tag('tr'):tag(ttype)
dataCell
:attr('colspan', 2)
:wikitext(s[i])
end
end
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(args.above)
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(args.below)
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
if args.child == 'yes' and args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
local marker = ''
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass('infobox')
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
marker = '[[' .. orphancat .. ']]'
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return marker .. tostring(root) .. marker
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
lq21lj4acq8hq4095hlnt7ceyb0odt0
803418
803417
2017-05-29T14:12:49Z
en>Frietjes
0
Undid revision 782833995 by [[Special:Contributions/Jc86035|Jc86035]] ([[User talk:Jc86035|talk]]) looks like this caused some serious problems, will require more testing (see, e.g, my talk page)
803418
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.header)
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:newline()
:wikitext(rowArgs.data)
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(args.above)
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(args.below)
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
if args.child == 'yes' and args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass('infobox')
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
edprki3cbc91qzciqck4g7yuj9k0n18
803419
803418
2017-06-05T13:55:32Z
en>Jc86035
0
merge from sandbox; still some pages with odd infoboxes over the place because of Tidy
803419
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:newline()
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
if args.child == 'yes' and args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass('infobox')
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
36dop1z7whmr9cvip6xt71vste6ng2e
803420
803419
2017-06-07T12:14:38Z
en>Jc86035
0
Undid revision 783927044 by [[Special:Contributions/Jc86035|Jc86035]] ([[User talk:Jc86035|talk]]) Doesn't play well with succession boxes
803420
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.header)
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:newline()
:wikitext(rowArgs.data)
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(args.above)
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(args.below)
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
if args.child == 'yes' and args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass('infobox')
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
edprki3cbc91qzciqck4g7yuj9k0n18
803421
803420
2017-06-17T07:53:56Z
en>Jc86035
0
try implementing again?
803421
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:newline()
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
if args.child == 'yes' and args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass('infobox')
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
h7ia1migmxaed0jrzap3fnixrgg2049
803422
803421
2018-01-24T22:39:59Z
en>Frietjes
0
infobox class not needed for subbox since the parent box will set the class (and causes problems on mobile)
803422
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:newline()
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
if args.child == 'yes' and args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
70c4sw4coyjec1b3vfiyyrfzu3bapv8
803423
803422
2018-07-06T13:05:33Z
en>Frietjes
0
add per https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481 see "two plain list" testcase
803423
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of * lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
s = mw.ustring.gsub(s, '([\r\n]%*[^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^(%*[^\r\n]*)$', '%1\n')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:newline()
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
if args.child == 'yes' and args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
035wwzvwt3y5u8arn7bo8n1hadi441i
803424
803423
2018-07-07T07:04:41Z
en>Jc86035
0
the three other types of lists as well
803424
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:newline()
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
if args.child == 'yes' and args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
3lqvq40va735qhmz9ozwwkwt6n87qbr
803425
803424
2018-08-26T15:49:54Z
en>Primefac
0
avoid "no data" tracking category when "child=yes" (see [[Template_talk:Infobox#Decat_by_default_when_child=yes?]])
803425
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:newline()
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
rp2tnb8ez8ttzoh83z4cf89fye7q9mo
803426
616540
2018-10-22T23:10:29Z
en>Frietjes
0
per talk page
803426
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:newline()
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:newline()
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
11ft3tooet55wlzmcb1gb28k7afkda4
803427
803426
2018-11-05T22:47:50Z
en>Frietjes
0
fix for spurious <p> tag per talk page
803427
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
fqwlmnh999j8i9ezhfasls5wwtsbxv1
803428
803427
2019-07-13T12:47:29Z
en>Gonnym
0
Added more options to Italic Title per discussion at [[Template talk:Infobox#Italics part deux]]
803428
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
--[[
Function handles the page's title italicization.
-- If the value of "italic title" is set to "force" or "yes", titles with no parentheses () are fully italicised,
and titles which contain parentheses are italicised before the first opening parenthesis.
-- If the value of "italic title" is set to "all", the complete title, including the text in parentheses is italicized.
-- If the value of "italic_string" is entered, instead of italicizing the whole title, it will italicize only some specific text.
-- Note that if the specific text is in the disambiguation then both |italic title=all and |italic_string=<text> will need to be used.
--
-- Optionale combinations:
--- No italics.
--- Italicized page name without disambiguation.
--- Italicized complete page name, including parentheses.
--- Italicized part of the text, not including disambiguation.
--- Italicized part of the text, including parentheses.
--]]
local function renderItalicTitle()
local italicTitleModule = require("Module:Italic title")._main
local italicTitleOption = args['italic title']
local italicTitleString = args['italic_string']
local italicTitleArg = {}
if (italicTitleOption) then
local italicLowercase = mw.ustring.lower(italicTitleOption)
if (italicLowercase == "no") then
-- If set to "no" or any other disabling parameter, do nothing.
return
elseif (italicLowercase == '' or italicLowercase == 'force' or italicLowercase == 'yes') then
root:wikitext(italicTitleModule({}))
return
elseif (italicLowercase == 'all') then
italicTitleArg.all = "yes"
else
-- Do nothing.
return
end
end
if (italicTitleString) then
italicTitleArg.string = italicTitleString
end
root:wikitext(italicTitleModule(italicTitleArg))
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('italic_string')
preprocessSingleArg('decat')
return _infobox()
end
return p
agn773u1y9zv50ljbgviq70vosaygkr
803429
803428
2019-07-13T13:05:36Z
en>Gonnym
0
Undid revision 906073835 by [[Special:Contributions/Gonnym|Gonnym]] ([[User talk:Gonnym|talk]]) errors in code were reported
803429
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
fqwlmnh999j8i9ezhfasls5wwtsbxv1
803430
803429
2020-05-13T14:37:24Z
en>Frietjes
0
tracking with [[Category:Pages which use infobox templates with ignored data cells]] for discussion on talk page
803430
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext('[[Category:Pages which use infobox templates with ignored data cells]]')
end
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
hmzwh371r9nydsvmocsdktbi7444zqo
803431
803430
2020-05-13T19:13:58Z
en>Frietjes
0
fix for categories triggering blank data cells per talk page (and spaces to tabs per edits by RexxS)
803431
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext('[[Category:Pages which use infobox templates with ignored data cells]]')
end
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local row_style
for k, num in ipairs(rownums) do
local dval = args['data' .. tostring(num)]
if dval and
dval:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]', ''):match('%S')
then
row_style = args['rowstyle' .. tostring(num)]
else
row_style = 'display:none'
end
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = row_style,
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
9hebohbiq10sn09rn6kzoelpztnif2q
803432
803431
2020-05-13T19:49:01Z
en>Galobtter
0
Undid revision 956509357 by [[Special:Contributions/Frietjes|Frietjes]] ([[User talk:Frietjes|talk]]) breaks headers in infobox
803432
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext('[[Category:Pages which use infobox templates with ignored data cells]]')
end
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
hmzwh371r9nydsvmocsdktbi7444zqo
803433
803432
2020-05-13T19:50:38Z
en>Frietjes
0
fixed
803433
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext('[[Category:Pages which use infobox templates with ignored data cells]]')
end
elseif rowArgs.data then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local row_style
for k, num in ipairs(rownums) do
local dval = args['data' .. tostring(num)]
row_style = args['rowstyle' .. tostring(num)]
if args['header' .. tostring(num)] then
row_style = args['rowstyle' .. tostring(num)]
elseif dval and
dval:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]', ''):match('%S')
then
row_style = args['rowstyle' .. tostring(num)]
else
row_style = 'display:none'
end
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = row_style,
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
6dsiqtdlz17hw0qgt0jrpxeupvlglq2
803434
803433
2020-05-13T20:01:15Z
en>Frietjes
0
simplify (tested in sandbox)
803434
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext('[[Category:Pages which use infobox templates with ignored data cells]]')
end
elseif rowArgs.data then
if not rowArgs.data:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]', ''):match('^%S') then
rowArgs.rowstyle = 'display:none'
end
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
7j4mp6z10ropygjyub7mtxdrh0n1rn3
803435
803434
2020-05-16T21:14:51Z
en>Ahecht
0
Allow arguments passed via #invoke to override those passed into the invoking template.
803435
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext('[[Category:Pages which use infobox templates with ignored data cells]]')
end
elseif rowArgs.data then
if not rowArgs.data:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]', ''):match('^%S') then
rowArgs.rowstyle = 'display:none'
end
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- Allow arguments passed via #invoke to override those passed into the invoking template.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
end
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
ogerbyi8ry8q8xs4bhtac9l6m6tc5tn
803436
803435
2020-05-16T21:23:56Z
en>Ahecht
0
Undid revision 957070706 by [[Special:Contributions/Ahecht|Ahecht]] ([[User talk:Ahecht|talk]]) - Undo for now, not very useful due to parameter name collisions
803436
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext('[[Category:Pages which use infobox templates with ignored data cells]]')
end
elseif rowArgs.data then
if not rowArgs.data:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]', ''):match('^%S') then
rowArgs.rowstyle = 'display:none'
end
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
return _infobox()
end
return p
7j4mp6z10ropygjyub7mtxdrh0n1rn3
803437
803436
2020-05-16T21:37:05Z
en>Ahecht
0
sync p.infoboxTemplate(frame) from sandbox
803437
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext('[[Category:Pages which use infobox templates with ignored data cells]]')
end
elseif rowArgs.data then
if not rowArgs.data:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]', ''):match('^%S') then
rowArgs.rowstyle = 'display:none'
end
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.parseDataParameters()
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame.args
end
p.parseDataParameters()
return _infobox()
end
function p.infoboxTemplate(frame)
-- For calling via #invoke within a template
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
p.parseDataParameters()
return _infobox()
end
return p
sphu312gk4rzj6aj5um0bkon03s0g5g
803438
803437
2020-05-16T23:04:02Z
en>Ahecht
0
fix Attempt to index upvalue 'origArgs' error
803438
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs = {}
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext('[[Category:Pages which use infobox templates with ignored data cells]]')
end
elseif rowArgs.data then
if not rowArgs.data:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]', ''):match('^%S') then
rowArgs.rowstyle = 'display:none'
end
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.parseDataParameters()
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame.args
end
p.parseDataParameters()
return _infobox()
end
function p.infoboxTemplate(frame)
-- For calling via #invoke within a template
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
p.parseDataParameters()
return _infobox()
end
return p
dgvpu6td21pdgys9xxz1582nb7k90de
803439
803438
2020-05-16T23:07:44Z
en>Ahecht
0
Fix "attempt to index upvalue 'origArgs' (a nil value)"
803439
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs = {}
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext('[[Category:Pages which use infobox templates with ignored data cells]]')
end
elseif rowArgs.data then
if not rowArgs.data:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]', ''):match('^%S') then
rowArgs.rowstyle = 'display:none'
end
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
local function parseDataParameters()
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
function p.infoboxTemplate(frame)
-- For calling via #invoke within a template
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
rqslxfr0ixpm41etqqc9cd2udbuwv4y
803440
803439
2020-06-25T19:04:15Z
en>Frietjes
0
per discussion on the talkpage, now supports |autoheaders=y
803440
Scribunto
text/plain
--
-- This module implements {{Infobox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local args = {}
local origArgs = {}
local root
local function notempty( s ) return s and s:match( '%S' ) end
local function fixChildBoxes(sval, tt)
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' .. subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' .. subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=849054481
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
local function union(t1, t2)
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
local function getArgNums(prefix)
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
local function addRow(rowArgs)
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:attr('id', rowArgs.rowid)
:tag('th')
:attr('colspan', 2)
:attr('id', rowArgs.headerid)
:addClass(rowArgs.class)
:addClass(args.headerclass)
:css('text-align', 'center')
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext('[[Category:Pages which use infobox templates with ignored data cells]]')
end
elseif rowArgs.data then
if not rowArgs.data:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]', ''):match('^%S') then
rowArgs.rowstyle = 'display:none'
end
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
row:attr('id', rowArgs.rowid)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:attr('id', rowArgs.labelid)
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
if not rowArgs.label then
dataCell
:attr('colspan', 2)
:css('text-align', 'center')
end
dataCell
:attr('id', rowArgs.dataid)
:addClass(rowArgs.class)
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass(args.titleclass)
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', 2)
:addClass(args.aboveclass)
:css('text-align', 'center')
:css('font-size', '125%')
:css('font-weight', 'bold')
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass(args.belowclass)
:css('text-align', 'center')
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addRow({
data = args['subheader' .. tostring(num)],
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:cssText(args.captionstyle)
:wikitext(caption)
end
addRow({
data = tostring(data),
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
local function preprocessRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and args['data' .. tostring(num)]:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]', ''):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]', ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
local function renderRows()
-- Gets the union of the header and data argument numbers,
-- and renders them all in order using addRow.
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)],
dataid = args['dataid' .. tostring(num)],
labelid = args['labelid' .. tostring(num)],
headerid = args['headerid' .. tostring(num)],
rowid = args['rowid' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'right')
:wikitext(navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
local function renderTrackingCategories()
if args.decat ~= 'yes' then
if args.child == 'yes' then
if args.title then
root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
end
local function _infobox()
-- Specify the overall layout of the infobox, with special settings
-- if the infobox is used as a 'child' inside another infobox.
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass((args.subbox ~= 'yes') and 'infobox' or nil)
:addClass(args.bodyclass)
if args.subbox == 'yes' then
root
:css('padding', '0')
:css('border', 'none')
:css('margin', '-3px')
:css('width', 'auto')
:css('min-width', '100%')
:css('font-size', '100%')
:css('clear', 'none')
:css('float', 'none')
:css('background-color', 'transparent')
else
root
:css('width', '22em')
end
root
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
if args.autoheaders then
preprocessRows()
end
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderTrackingCategories()
return tostring(root)
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
local function parseDataParameters()
-- Parse the data parameters in the same order that the old {{infobox}} did, so that
-- references etc. will display in the expected places. Parameters that depend on
-- another parameter are only processed if that parameter is present, to avoid
-- phantom references appearing in article reference lists.
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'},
{prefix = 'dataid'},
{prefix = 'labelid'},
{prefix = 'headerid'},
{prefix = 'rowid'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
preprocessSingleArg('decat')
end
function p.infobox(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
function p.infoboxTemplate(frame)
-- For calling via #invoke within a template
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
icv7hicbyi1nka7zvdkn0xjgl2xiiia
803441
803440
2021-03-31T19:24:48Z
en>Izno
0
add using-infobox tstyles support
803441
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages which use embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: load base templatestyles here rather than in MediaWiki:Common.css
We aren't doing it here yet because there are 4-5000 pages with 'raw' infobox
tables. See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
-- local base_templatestyles = frame:extensionTag{
-- name = 'templatestyles', args = { src = cfg.i18n.templatestyles }
-- }
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
-- base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
return loadTemplateStyles() .. tostring(root)
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
e3cfyoal7a38ou4omgcpf2j7vsh8hq2
803442
803441
2021-07-12T21:56:48Z
en>Frietjes
0
fix for {{infobox}} with no input args generating an empty <table class="infobox"></table> per talk page
803442
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages which use embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: load base templatestyles here rather than in MediaWiki:Common.css
We aren't doing it here yet because there are 4-5000 pages with 'raw' infobox
tables. See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
-- local base_templatestyles = frame:extensionTag{
-- name = 'templatestyles', args = { src = cfg.i18n.templatestyles }
-- }
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
-- base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
gjmhtnj4qfymzr0fe6nr73pcgags5bu
803443
803442
2021-09-08T18:11:29Z
en>Izno
0
nix
803443
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages which use embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: load base templatestyles here rather than in MediaWiki:Common.css
We aren't doing it here yet because there are 4-5000 pages with 'raw' infobox
tables. See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
-- local base_templatestyles = frame:extensionTag{
-- name = 'templatestyles', args = { src = cfg.i18n.templatestyles }
-- }
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
-- base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
structure_infobox_common()
return loadTemplateStyles() .. root
else
root = mw.html.create()
root
:wikitext(args.title)
structure_infobox_common()
return root
end
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
nouue8knrrryohtsx43expp0hvz9k5a
803444
803443
2021-09-29T14:05:49Z
en>Frietjes
0
(1) return templatestyles when child=yes, (2) move templatestyles and categories before </tr> to avoid creating empty cells (see thread on talk page)
803444
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(<templatestyles[^<>]*>%s*</templatestyles>)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages which use embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: load base templatestyles here rather than in MediaWiki:Common.css
We aren't doing it here yet because there are 4-5000 pages with 'raw' infobox
tables. See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
-- local base_templatestyles = frame:extensionTag{
-- name = 'templatestyles', args = { src = cfg.i18n.templatestyles }
-- }
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
-- base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
kmblpziwagns5xikxi3vvxz4z9i1bwx
803445
803444
2021-10-07T14:44:35Z
en>Izno
0
move subbox to templatestyles, still working on the other 4k. will remove from common.css Soon
803445
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(<templatestyles[^<>]*>%s*</templatestyles>)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages which use embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: FINISH loading base templatestyles here rather than in
MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables.
See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' }
}
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
cyfohv8e3uo359m1yfdtqd6lfpa0km6
803446
803445
2021-10-08T12:17:23Z
en>Neveselbert
0
Reverted [[WP:AGF|good faith]] edits by [[Special:Contributions/Izno|Izno]] ([[User talk:Izno|talk]]): This edit appears to cause extraordinarily high spaces between lines on [[Template:Infobox officeholder]].
803446
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(<templatestyles[^<>]*>%s*</templatestyles>)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages which use embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: load base templatestyles here rather than in MediaWiki:Common.css
We aren't doing it here yet because there are 4-5000 pages with 'raw' infobox
tables. See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
-- local base_templatestyles = frame:extensionTag{
-- name = 'templatestyles', args = { src = cfg.i18n.templatestyles }
-- }
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
-- base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
kmblpziwagns5xikxi3vvxz4z9i1bwx
803447
803446
2021-10-10T17:00:17Z
en>Plastikspork
0
Fix templatestyles pattern, this tag will be expanded into a strip-marker when processed here
803447
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages which use embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: load base templatestyles here rather than in MediaWiki:Common.css
We aren't doing it here yet because there are 4-5000 pages with 'raw' infobox
tables. See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
-- local base_templatestyles = frame:extensionTag{
-- name = 'templatestyles', args = { src = cfg.i18n.templatestyles }
-- }
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
-- base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
7vhoomboweqig59rxhawfsxxzqb6nem
803448
803447
2021-10-11T22:21:33Z
en>Izno
0
uno mas, based on plastikspork's work
803448
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages which use embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: FINISH loading base templatestyles here rather than in
MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables.
See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' }
}
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
j79y42zpoooozmpk71k415bkisxo26u
803449
803448
2021-10-11T22:22:54Z
en>Izno
0
ocd triggered
803449
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages which use embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: FINISH loading base templatestyles here rather than in
MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables.
See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' }
}
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
mub4z1wjimlb7sij0tmufeqdgcb806l
803450
803449
2021-11-16T10:21:29Z
en>Fayenatic london
0
update category link
803450
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages which use embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles using infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: FINISH loading base templatestyles here rather than in
MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables.
See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' }
}
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
8h8sbljlvritz7cg0qmoxakh4ui4tx8
803451
803450
2021-11-16T10:26:17Z
en>Fayenatic london
0
update category link
803451
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages using embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles using infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: FINISH loading base templatestyles here rather than in
MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables.
See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' }
}
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
neykqi4g4ir7sm57csrncxrrv016oun
803452
803451
2021-11-16T10:29:12Z
en>Fayenatic london
0
update category link
803452
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages using infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages using embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles using infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: FINISH loading base templatestyles here rather than in
MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables.
See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' }
}
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
dqqnr7l5kr5hmj0mxjqtzu7mvoyat9b
803453
803452
2021-11-16T10:29:48Z
en>Fayenatic london
0
spacing
803453
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages using infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages using embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles using infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: FINISH loading base templatestyles here rather than in
MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables.
See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' }
}
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
0nrwbmsjupox6pg5hh6nvlk9r7dcw81
803454
803453
2022-12-20T20:41:06Z
en>Izno
0
support finding plainlist in infobox class fields
803454
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local lists = {
plainlist_t = {
patterns = {
'^plainlist$',
'%splainlist$',
'^plainlist%s',
'%splainlist%s'
},
found = false,
styles = 'Plainlist/styles.css'
}
-- TODO: hlist_t goes here when ready to be TStyled, plus relevant
-- implementations below
}
local function has_list_class(args_to_check)
for _, list in pairs(lists) do
if not list.found then
for _, arg in pairs(args_to_check) do
for _, pattern in ipairs(list.patterns) do
if mw.ustring.find(arg or '', pattern) then
list.found = true
break
end
end
if list.found then break end
end
end
end
end
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
has_list_class({ rowArgs.rowclass, rowArgs.class, args.headerclass })
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages using infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ rowArgs.rowclass, rowArgs.class })
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
has_list_class({args.titleclass})
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
has_list_class({ args.aboveclass })
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
has_list_class({ args.belowclass })
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ subheaderArgs.rowclass, subheaderArgs.class })
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ imageArgs.rowclass, imageArgs.class })
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(require('Module:Italic title')._main({}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages using embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles using infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: FINISH loading base templatestyles here rather than in
MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables.
See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' }
}
local templatestyles = ''
if args['templatestyles'] then
templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then
child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then
grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
local plainlist_templatestyles = ''
if lists.plainlist_t.found then
plainlist_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = lists.plainlist_t.styles }
}
end
return table.concat({
base_templatestyles, -- see function description
templatestyles,
child_templatestyles,
grandchild_templatestyles,
plainlist_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
has_list_class({ args.bodyclass })
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
n7l5q18lc0pbmi0pb1ynzmkgxi2kqcv
803455
803454
2022-12-27T21:29:12Z
en>Izno
0
merge hlist here
803455
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local lists = {
plainlist_t = {
patterns = {
'^plainlist$',
'%splainlist$',
'^plainlist%s',
'%splainlist%s'
},
found = false,
styles = 'Plainlist/styles.css'
},
hlist_t = {
patterns = {
'^hlist$',
'%shlist$',
'^hlist%s',
'%shlist%s'
},
found = false,
styles = 'Hlist/styles.css'
}
}
local function has_list_class(args_to_check)
for _, list in pairs(lists) do
if not list.found then
for _, arg in pairs(args_to_check) do
for _, pattern in ipairs(list.patterns) do
if mw.ustring.find(arg or '', pattern) then
list.found = true
break
end
end
if list.found then break end
end
end
end
end
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
has_list_class({ rowArgs.rowclass, rowArgs.class, args.headerclass })
root
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages using infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ rowArgs.rowclass, rowArgs.class })
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
has_list_class({args.titleclass})
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
has_list_class({ args.aboveclass })
root
:tag('tr')
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
has_list_class({ args.belowclass })
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ subheaderArgs.rowclass, subheaderArgs.class })
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ imageArgs.rowclass, imageArgs.class })
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(require('Module:Italic title')._main({}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if args.decat == 'yes' then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages using embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles using infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: FINISH loading base templatestyles here rather than in
MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables.
See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
local hlist_templatestyles = ''
if lists.hlist_t.found then
hlist_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = lists.hlist_t.styles }
}
end
local plainlist_templatestyles = ''
if lists.plainlist_t.found then
plainlist_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = lists.plainlist_t.styles }
}
end
-- See function description
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' }
}
local templatestyles = ''
if args['templatestyles'] then
templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then
child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then
grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
-- hlist -> plainlist -> base is best-effort to preserve old Common.css ordering.
-- this ordering is not a guarantee because the rows of interest invoking
-- each class may not be on a specific page
hlist_templatestyles,
plainlist_templatestyles,
base_templatestyles,
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
has_list_class({ args.bodyclass })
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
1m9tzydwofr8cj9bzzm2am6moool42y
803456
796825
2026-06-08T00:50:22Z
en>Zackmann08
0
hiding un-needed blank rows that show up in child infoboxes per [[Template_talk:Infobox#Blank_rows_on_child_infoboxes....|talk page]]
803456
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local yesno = require("Module:Yesno")
local lists = {
plainlist_t = {
patterns = {
'^plainlist$',
'%splainlist$',
'^plainlist%s',
'%splainlist%s'
},
found = false,
styles = 'Plainlist/styles.css'
},
hlist_t = {
patterns = {
'^hlist$',
'%shlist$',
'^hlist%s',
'%shlist%s'
},
found = false,
styles = 'Hlist/styles.css'
}
}
local function has_list_class(args_to_check)
for _, list in pairs(lists) do
if not list.found then
for _, arg in pairs(args_to_check) do
for _, pattern in ipairs(list.patterns) do
if mw.ustring.find(arg or '', pattern) then
list.found = true
break
end
end
if list.found then break end
end
end
end
end
local function isUntitledChildBox(sval)
return sval and ( sval:match( '^%s*<%s*[Tt][Rr]' ) or sval:match( '^%s*\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127%s*<%s*[Tt][Rr]' ) )
end
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
has_list_class({ rowArgs.rowclass, rowArgs.class, args.headerclass })
root
:tag('tr')
:addClass(rowArgs.rowclass)
:addClass( isUntitledChildBox( rowArgs.header ) and 'infobox-hiddenrow' or nil )
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data and not yesno(args.decat) then
root:wikitext(
'[[Category:Pages using infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ rowArgs.rowclass, rowArgs.class })
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
else
row:addClass( isUntitledChildBox( rowArgs.data ) and 'infobox-hiddenrow' or nil )
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
has_list_class({args.titleclass})
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
has_list_class({ args.aboveclass })
root
:tag('tr')
:addClass( isUntitledChildBox( args.above ) and 'infobox-hiddenrow' or nil )
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
has_list_class({ args.belowclass })
root
:tag('tr')
:addClass( isUntitledChildBox( args.below ) and 'infobox-hiddenrow' or nil )
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ subheaderArgs.rowclass, subheaderArgs.class })
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
row:addClass( isUntitledChildBox( subheaderArgs.data ) and 'infobox-hiddenrow' or nil )
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ imageArgs.rowclass, imageArgs.class })
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
row:addClass( isUntitledChildBox( imageArgs.data ) and 'infobox-hiddenrow' or nil )
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(require('Module:Italic title')._main({}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if yesno(args.decat) then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages using embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles using infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: FINISH loading base templatestyles here rather than in
MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables.
See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
local hlist_templatestyles = ''
if lists.hlist_t.found then
hlist_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = lists.hlist_t.styles }
}
end
local plainlist_templatestyles = ''
if lists.plainlist_t.found then
plainlist_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = lists.plainlist_t.styles }
}
end
-- See function description
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' }
}
local templatestyles = ''
if args['templatestyles'] then
templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then
child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then
grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
-- hlist -> plainlist -> base is best-effort to preserve old Common.css ordering.
-- this ordering is not a guarantee because the rows of interest invoking
-- each class may not be on a specific page
hlist_templatestyles,
plainlist_templatestyles,
base_templatestyles,
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
has_list_class({ args.bodyclass })
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
kueb5p6xeoq6x7bxu2zyyl7pmxgesrs
803457
803456
2026-07-29T23:47:08Z
SM7
3953
42 revisions imported from [[:en:Module:Infobox]]
803456
Scribunto
text/plain
local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'
local has_rows = false
local yesno = require("Module:Yesno")
local lists = {
plainlist_t = {
patterns = {
'^plainlist$',
'%splainlist$',
'^plainlist%s',
'%splainlist%s'
},
found = false,
styles = 'Plainlist/styles.css'
},
hlist_t = {
patterns = {
'^hlist$',
'%shlist$',
'^hlist%s',
'%shlist%s'
},
found = false,
styles = 'Hlist/styles.css'
}
}
local function has_list_class(args_to_check)
for _, list in pairs(lists) do
if not list.found then
for _, arg in pairs(args_to_check) do
for _, pattern in ipairs(list.patterns) do
if mw.ustring.find(arg or '', pattern) then
list.found = true
break
end
end
if list.found then break end
end
end
end
end
local function isUntitledChildBox(sval)
return sval and ( sval:match( '^%s*<%s*[Tt][Rr]' ) or sval:match( '^%s*\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127%s*<%s*[Tt][Rr]' ) )
end
local function fixChildBoxes(sval, tt)
local function notempty( s ) return s and s:match( '%S' ) end
if notempty(sval) then
local marker = '<span class=special_infobox_marker>'
local s = sval
-- start moving templatestyles and categories inside of table rows
local slast = ''
while slast ~= s do
slast = s
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*%]%])', '%2%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>%s*)(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)', '%2%1')
end
-- end moving templatestyles and categories inside of table rows
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
if s:match(marker) then
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
end
if s:match(marker) then
local subcells = mw.text.split(s, marker)
s = ''
for k = 1, #subcells do
if k == 1 then
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
elseif k == #subcells then
local rowstyle = ' style="display:none"'
if notempty(subcells[k]) then rowstyle = '' end
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
subcells[k]
elseif notempty(subcells[k]) then
if (k % 2) == 0 then
s = s .. subcells[k]
else
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
subcells[k] .. '</' .. tt .. '></tr>'
end
end
end
end
-- the next two lines add a newline at the end of lists for the PHP parser
-- [[Special:Diff/849054481]]
-- remove when [[:phab:T191516]] is fixed or OBE
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
return s
else
return sval
end
end
-- Cleans empty tables
local function cleanInfobox()
root = tostring(root)
if has_rows == false then
root = mw.ustring.gsub(root, '<table[^<>]*>%s*</table>', '')
end
end
-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)
local vals = {}
for k, v in pairs(t1) do
vals[v] = true
end
for k, v in pairs(t2) do
vals[v] = true
end
local ret = {}
for k, v in pairs(vals) do
table.insert(ret, k)
end
return ret
end
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
table.sort(nums)
return nums
end
-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
has_rows = true
has_list_class({ rowArgs.rowclass, rowArgs.class, args.headerclass })
root
:tag('tr')
:addClass(rowArgs.rowclass)
:addClass( isUntitledChildBox( rowArgs.header ) and 'infobox-hiddenrow' or nil )
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data and not yesno(args.decat) then
root:wikitext(
'[[Category:Pages using infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ rowArgs.rowclass, rowArgs.class })
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
else
row:addClass( isUntitledChildBox( rowArgs.data ) and 'infobox-hiddenrow' or nil )
end
local dataCell = row:tag('td')
dataCell
:attr('colspan', not rowArgs.label and '2' or nil)
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
local function renderTitle()
if not args.title then return end
has_rows = true
has_list_class({args.titleclass})
root
:tag('caption')
:addClass('infobox-title')
:addClass(args.titleclass)
-- @deprecated next; target .infobox-<name> .infobox-title
:cssText(args.titlestyle)
:wikitext(args.title)
end
local function renderAboveRow()
if not args.above then return end
has_rows = true
has_list_class({ args.aboveclass })
root
:tag('tr')
:addClass( isUntitledChildBox( args.above ) and 'infobox-hiddenrow' or nil )
:tag('th')
:attr('colspan', '2')
:addClass('infobox-above')
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
local function renderBelowRow()
if not args.below then return end
has_rows = true
has_list_class({ args.belowclass })
root
:tag('tr')
:addClass( isUntitledChildBox( args.below ) and 'infobox-hiddenrow' or nil )
:tag('td')
:attr('colspan', '2')
:addClass('infobox-below')
:addClass(args.belowclass)
-- @deprecated next; target .infobox-<name> .infobox-below
:cssText(args.belowstyle)
:wikitext(fixChildBoxes(args.below,'td'))
end
local function addSubheaderRow(subheaderArgs)
if subheaderArgs.data and
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ subheaderArgs.rowclass, subheaderArgs.class })
local row = root:tag('tr')
row:addClass(subheaderArgs.rowclass)
row:addClass( isUntitledChildBox( subheaderArgs.data ) and 'infobox-hiddenrow' or nil )
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-subheader')
:addClass(subheaderArgs.class)
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
local function renderSubheaders()
if args.subheader then
args.subheader1 = args.subheader
end
if args.subheaderrowclass then
args.subheaderrowclass1 = args.subheaderrowclass
end
local subheadernums = getArgNums('subheader')
for k, num in ipairs(subheadernums) do
addSubheaderRow({
data = args['subheader' .. tostring(num)],
-- @deprecated next; target .infobox-<name> .infobox-subheader
datastyle = args.subheaderstyle,
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
local function addImageRow(imageArgs)
if imageArgs.data and
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
has_rows = true
has_list_class({ imageArgs.rowclass, imageArgs.class })
local row = root:tag('tr')
row:addClass(imageArgs.rowclass)
row:addClass( isUntitledChildBox( imageArgs.data ) and 'infobox-hiddenrow' or nil )
local dataCell = row:tag('td')
dataCell
:attr('colspan', '2')
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
local function renderImages()
if args.image then
args.image1 = args.image
end
if args.caption then
args.caption1 = args.caption
end
local imagenums = getArgNums('image')
for k, num in ipairs(imagenums) do
local caption = args['caption' .. tostring(num)]
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
if caption then
data
:tag('div')
:addClass('infobox-caption')
-- @deprecated next; target .infobox-<name> .infobox-caption
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
if not args.autoheaders then return end
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
end
-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
for k, num in ipairs(rownums) do
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
local function renderNavBar()
if not args.name then return end
has_rows = true
root
:tag('tr')
:tag('td')
:attr('colspan', '2')
:addClass('infobox-navbar')
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
})
end
local function renderItalicTitle()
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
root:wikitext(require('Module:Italic title')._main({}))
end
end
-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
if yesno(args.decat) then return end
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages using embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles using infobox templates with no data rows]]')
end
end
--[=[
Loads the templatestyles for the infobox.
TODO: FINISH loading base templatestyles here rather than in
MediaWiki:Common.css. There are 4-5000 pages with 'raw' infobox tables.
See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
local hlist_templatestyles = ''
if lists.hlist_t.found then
hlist_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = lists.hlist_t.styles }
}
end
local plainlist_templatestyles = ''
if lists.plainlist_t.found then
plainlist_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = lists.plainlist_t.styles }
}
end
-- See function description
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Infobox/styles.css' }
}
local templatestyles = ''
if args['templatestyles'] then
templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
local child_templatestyles = ''
if args['child templatestyles'] then
child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end
local grandchild_templatestyles = ''
if args['grandchild templatestyles'] then
grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end
return table.concat({
-- hlist -> plainlist -> base is best-effort to preserve old Common.css ordering.
-- this ordering is not a guarantee because the rows of interest invoking
-- each class may not be on a specific page
hlist_templatestyles,
plainlist_templatestyles,
base_templatestyles,
templatestyles,
child_templatestyles,
grandchild_templatestyles
})
end
-- common functions between the child and non child cases
local function structure_infobox_common()
renderSubheaders()
renderImages()
preprocessRows()
renderRows()
renderBelowRow()
renderNavBar()
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()
cleanInfobox()
end
-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
if args.child ~= 'yes' then
root = mw.html.create('table')
root
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
:addClass(args.bodyclass)
-- @deprecated next; target .infobox-<name>
:cssText(args.bodystyle)
has_list_class({ args.bodyclass })
renderTitle()
renderAboveRow()
else
root = mw.html.create()
root
:wikitext(args.title)
end
structure_infobox_common()
return loadTemplateStyles() .. root
end
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
-- Do another loop if any arguments are found, even blank ones.
moreArgumentsExist = true
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present
-- and not blank, or we are processing "prefix1" and "prefix" is
-- present and not blank, and if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()
preprocessSingleArg('autoheaders')
preprocessSingleArg('child')
preprocessSingleArg('bodyclass')
preprocessSingleArg('subbox')
preprocessSingleArg('bodystyle')
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
parseDataParameters()
return _infobox()
end
-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
origArgs = {}
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
parseDataParameters()
return _infobox()
end
return p
kueb5p6xeoq6x7bxu2zyyl7pmxgesrs
टेम्पलेट:Infobox company
10
28448
803459
783217
2025-05-02T17:16:49Z
en>Rich Farmbrough
0
Add incorporated field
803459
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{#if:{{{name|}}} | {{{name}}} | {{#if:{{{company_name|}}}|{{{company_name}}}|<includeonly>{{PAGENAMEBASE}}</includeonly>}} }} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{{logo|{{{company_logo|}}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{{logo|{{{company_logo|}}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{{logo_upright|1}}} |alt={{{logo_alt|{{{alt|}}}}}} |class= {{{logo_class|}}} }}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |sizedefault=frameless |upright={{{image_upright|1}}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{{trade_name|{{{trading_name|}}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{{former_names|{{{former_name|}}}}}}
| label5 = Company type
| class5 = category
| data5 = {{{type|{{{company_type|}}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{predecessors|{{{predecessor|}}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{incorporated|{{{incorporation|}}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founded|{{{foundation|}}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founders|{{{founder|}}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{defunct|{{{dissolved|}}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{successors|{{{successor|}}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|{{{location|}}}}}} | label | adr}}
| data17 = {{#if:{{{hq_location|{{{location|}}}}}}{{{hq_location_city|{{{location_city|}}}}}}{{{hq_location_country|{{{location_country|}}}}}}
| {{Comma separated entries
| 1 = {{{hq_location|{{{location|}}}}}}
| 2 = {{#if:{{{hq_location_city|}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{{hq_location_city|{{{location_city|}}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{{hq_location_country|{{{location_country|}}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|{{{locations|}}}}}} | {{{num_locations|{{{locations|}}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{{areas_served|{{{area_served|}}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{net_income|{{{profit|}}}}}}{{#if:{{{net_income_year|{{{profit_year|}}}}}} | ({{{net_income_year|{{{profit_year|}}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{{owners|{{{owner|}}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}} | {{{members}}}{{#if:{{{members_year|}}} | ({{{members_year}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{{subsid|}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|{{{homepage|}}}}}} |{{{website|{{{homepage|}}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}|{{{logo|{{{company_logo|}}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for clobbered parameters|check|nested=1|template=Infobox company|cat={{main other|Category:Pages using infobox company with conflicting parameters}}|name; company_name|logo; company_logo|logo_alt; alt|trade_name; trading_name|former_names; former_name|type; company_type|predecessors; predecessor|successors; successor|foundation; founded|founders; founder|defunct; dissolved|hq_location; location|hq_location_city; location_city|hq_location_country; location_country|num_locations; locations|areas_served; area_served|net_income; profit|net_income_year; profit_year|owners; owner |homepage; website
}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories }}<noinclude>
{{documentation}}
</noinclude>
g6uuqljm2wzl9ehj86kj5xe4bhlt1ul
803460
803459
2025-09-05T00:14:20Z
en>Zackmann08
0
/* top */ adding support for headquarters which is consistently being used.
803460
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{#if:{{{name|}}} | {{{name}}} | {{#if:{{{company_name|}}}|{{{company_name}}}|<includeonly>{{PAGENAMEBASE}}</includeonly>}} }} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{{logo|{{{company_logo|}}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{{logo|{{{company_logo|}}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{{logo_upright|1}}} |alt={{{logo_alt|{{{alt|}}}}}} |class= {{{logo_class|}}} }}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |sizedefault=frameless |upright={{{image_upright|1}}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{{trade_name|{{{trading_name|}}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{{former_names|{{{former_name|}}}}}}
| label5 = Company type
| class5 = category
| data5 = {{{type|{{{company_type|}}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{predecessors|{{{predecessor|}}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{incorporated|{{{incorporation|}}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founded|{{{foundation|}}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founders|{{{founder|}}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{defunct|{{{dissolved|}}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{successors|{{{successor|}}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}} | label | adr}}
| data17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}}{{{hq_location_city|{{{location_city|}}}}}}{{{hq_location_country|{{{location_country|}}}}}}
| {{Comma separated entries
| 1 = {{{hq_location|{{{location|}}}}}}
| 2 = {{#if:{{{hq_location_city|{{{headquarters|}}}}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{{hq_location_city|{{{location_city|}}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{{hq_location_country|{{{location_country|}}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|{{{locations|}}}}}} | {{{num_locations|{{{locations|}}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{{areas_served|{{{area_served|}}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{net_income|{{{profit|}}}}}}{{#if:{{{net_income_year|{{{profit_year|}}}}}} | ({{{net_income_year|{{{profit_year|}}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{{owners|{{{owner|}}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}} | {{{members}}}{{#if:{{{members_year|}}} | ({{{members_year}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{{subsid|}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|{{{homepage|}}}}}} |{{{website|{{{homepage|}}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}|{{{logo|{{{company_logo|}}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for clobbered parameters|check|nested=1|template=Infobox company|cat={{main other|Category:Pages using infobox company with conflicting parameters}}|name; company_name|logo; company_logo|logo_alt; alt|trade_name; trading_name|former_names; former_name|type; company_type|predecessors; predecessor|successors; successor|foundation; founded|founders; founder|defunct; dissolved|hq_location; location|hq_location_city; location_city|hq_location_country; location_country|num_locations; locations|areas_served; area_served|net_income; profit|net_income_year; profit_year|owners; owner |homepage; website
}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories }}<noinclude>
{{documentation}}
</noinclude>
1tdb4rb7vuefu7w853n2ld2g8uqur0n
803461
803460
2025-09-05T23:38:59Z
en>Zackmann08
0
Adding support for subsidiaries Which keeps popping up
803461
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{#if:{{{name|}}} | {{{name}}} | {{#if:{{{company_name|}}}|{{{company_name}}}|<includeonly>{{PAGENAMEBASE}}</includeonly>}} }} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{{logo|{{{company_logo|}}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{{logo|{{{company_logo|}}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{{logo_upright|1}}} |alt={{{logo_alt|{{{alt|}}}}}} |class= {{{logo_class|}}} }}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |sizedefault=frameless |upright={{{image_upright|1}}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{{trade_name|{{{trading_name|}}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{{former_names|{{{former_name|}}}}}}
| label5 = Company type
| class5 = category
| data5 = {{{type|{{{company_type|}}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{predecessors|{{{predecessor|}}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{incorporated|{{{incorporation|}}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founded|{{{foundation|}}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founders|{{{founder|}}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{defunct|{{{dissolved|}}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{successors|{{{successor|}}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}} | label | adr}}
| data17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}}{{{hq_location_city|{{{location_city|}}}}}}{{{hq_location_country|{{{location_country|}}}}}}
| {{Comma separated entries
| 1 = {{{hq_location|{{{location|}}}}}}
| 2 = {{#if:{{{hq_location_city|{{{headquarters|}}}}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{{hq_location_city|{{{location_city|}}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{{hq_location_country|{{{location_country|}}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|{{{locations|}}}}}} | {{{num_locations|{{{locations|}}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{{areas_served|{{{area_served|}}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{net_income|{{{profit|}}}}}}{{#if:{{{net_income_year|{{{profit_year|}}}}}} | ({{{net_income_year|{{{profit_year|}}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{{owners|{{{owner|}}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}} | {{{members}}}{{#if:{{{members_year|}}} | ({{{members_year}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{{subsid|{{{subsidiaries|}}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|{{{homepage|}}}}}} |{{{website|{{{homepage|}}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}|{{{logo|{{{company_logo|}}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for clobbered parameters|check|nested=1|template=Infobox company|cat={{main other|Category:Pages using infobox company with conflicting parameters}}|name; company_name|logo; company_logo|logo_alt; alt|trade_name; trading_name|former_names; former_name|type; company_type|predecessors; predecessor|successors; successor|foundation; founded|founders; founder|defunct; dissolved|hq_location; location|hq_location_city; location_city|hq_location_country; location_country|num_locations; locations|areas_served; area_served|net_income; profit|net_income_year; profit_year|owners; owner |homepage; website
}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories }}<noinclude>
{{documentation}}
</noinclude>
fmaz6gykehhkkhubmi3u9qwuzbpl5o5
803462
803461
2025-09-05T23:49:52Z
en>Zackmann08
0
/* top */
803462
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{#if:{{{name|}}} | {{{name}}} | {{#if:{{{company_name|}}}|{{{company_name}}}|<includeonly>{{PAGENAMEBASE}}</includeonly>}} }} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{{logo|{{{company_logo|}}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{{logo|{{{company_logo|}}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{{logo_upright|1}}} |alt={{{logo_alt|{{{alt|}}}}}} |class= {{{logo_class|}}} }}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |sizedefault=frameless |upright={{{image_upright|1}}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{{trade_name|{{{trading_name|}}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{{former_names|{{{former_name|}}}}}}
| label5 = Company type
| class5 = category
| data5 = {{{type|{{{company_type|}}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{predecessors|{{{predecessor|}}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{incorporated|{{{incorporation|}}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founded|{{{foundation|}}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founders|{{{founder|}}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{defunct|{{{dissolved|}}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{successors|{{{successor|}}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}} | label | adr}}
| data17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}}{{{hq_location_city|{{{location_city|}}}}}}{{{hq_location_country|{{{location_country|}}}}}}
| {{Comma separated entries
| 1 = {{{hq_location|{{{location|}}}}}}
| 2 = {{#if:{{{hq_location_city|{{{headquarters|}}}}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{{hq_location_city|{{{location_city|}}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{{hq_location_country|{{{location_country|}}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|{{{locations|}}}}}} | {{{num_locations|{{{locations|}}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{{areas_served|{{{area_served|}}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{net_income|{{{profit|}}}}}}{{#if:{{{net_income_year|{{{profit_year|}}}}}} | ({{{net_income_year|{{{profit_year|}}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{{owners|{{{owner|}}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}} | {{{members}}}{{#if:{{{members_year|}}} | ({{{members_year}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{{subsid|{{{subsidiaries|}}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|{{{homepage|}}}}}} |{{{website|{{{homepage|}}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}|{{{logo|{{{company_logo|}}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for clobbered parameters|check|nested=1|template=Infobox company|cat={{main other|Category:Pages using infobox company with conflicting parameters}}|name; company_name|logo; company_logo|logo_alt; alt|trade_name; trading_name|former_names; former_name|type; company_type|predecessors; predecessor|successors; successor|foundation; founded|founders; founder|defunct; dissolved|hq_location; location|hq_location_city; location_city|hq_location_country; location_country|num_locations; locations|areas_served; area_served|net_income; profit|net_income_year; profit_year|owners; owner |homepage; website
}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories }}<noinclude>
{{documentation}}
</noinclude>
ia9wgm4nycg114mnftmhq0lcwnnr1ev
803463
803462
2025-09-30T13:57:13Z
en>Hike395
0
rm non-functional parameter
803463
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{#if:{{{name|}}} | {{{name}}} | {{#if:{{{company_name|}}}|{{{company_name}}}|<includeonly>{{PAGENAMEBASE}}</includeonly>}} }} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{{logo|{{{company_logo|}}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{{logo|{{{company_logo|}}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{{logo_upright|1}}} |alt={{{logo_alt|{{{alt|}}}}}} |class= {{{logo_class|}}} }}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{{image_upright|1}}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{{trade_name|{{{trading_name|}}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{{former_names|{{{former_name|}}}}}}
| label5 = Company type
| class5 = category
| data5 = {{{type|{{{company_type|}}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{predecessors|{{{predecessor|}}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{incorporated|{{{incorporation|}}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founded|{{{foundation|}}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founders|{{{founder|}}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{defunct|{{{dissolved|}}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{successors|{{{successor|}}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}} | label | adr}}
| data17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}}{{{hq_location_city|{{{location_city|}}}}}}{{{hq_location_country|{{{location_country|}}}}}}
| {{Comma separated entries
| 1 = {{{hq_location|{{{location|}}}}}}
| 2 = {{#if:{{{hq_location_city|{{{headquarters|}}}}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{{hq_location_city|{{{location_city|}}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{{hq_location_country|{{{location_country|}}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|{{{locations|}}}}}} | {{{num_locations|{{{locations|}}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{{areas_served|{{{area_served|}}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{net_income|{{{profit|}}}}}}{{#if:{{{net_income_year|{{{profit_year|}}}}}} | ({{{net_income_year|{{{profit_year|}}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{{owners|{{{owner|}}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}} | {{{members}}}{{#if:{{{members_year|}}} | ({{{members_year}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{{subsid|{{{subsidiaries|}}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|{{{homepage|}}}}}} |{{{website|{{{homepage|}}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}|{{{logo|{{{company_logo|}}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for clobbered parameters|check|nested=1|template=Infobox company|cat={{main other|Category:Pages using infobox company with conflicting parameters}}|name; company_name|logo; company_logo|logo_alt; alt|trade_name; trading_name|former_names; former_name|type; company_type|predecessors; predecessor|successors; successor|foundation; founded|founders; founder|defunct; dissolved|hq_location; location|hq_location_city; location_city|hq_location_country; location_country|num_locations; locations|areas_served; area_served|net_income; profit|net_income_year; profit_year|owners; owner |homepage; website
}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories }}<noinclude>
{{documentation}}
</noinclude>
po1rzgo25leduhy09jz6r9jnxk4wjdu
803464
803463
2025-10-01T06:37:57Z
en>Zackmann08
0
fixing error where {{{headquarters|}}} Was not actually displaying
803464
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{#if:{{{name|}}} | {{{name}}} | {{#if:{{{company_name|}}}|{{{company_name}}}|<includeonly>{{PAGENAMEBASE}}</includeonly>}} }} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{{logo|{{{company_logo|}}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{{logo|{{{company_logo|}}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{{logo_upright|1}}} |alt={{{logo_alt|{{{alt|}}}}}} |class= {{{logo_class|}}} }}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{{image_upright|1}}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{{trade_name|{{{trading_name|}}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{{former_names|{{{former_name|}}}}}}
| label5 = Company type
| class5 = category
| data5 = {{{type|{{{company_type|}}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{predecessors|{{{predecessor|}}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{incorporated|{{{incorporation|}}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founded|{{{foundation|}}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founders|{{{founder|}}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{defunct|{{{dissolved|}}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{successors|{{{successor|}}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}} | label | adr}}
| data17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}}{{{hq_location_city|{{{location_city|}}}}}}{{{hq_location_country|{{{location_country|}}}}}}
| {{Comma separated entries
| 1 = {{{hq_location|{{{location|{{{headquarters|}}}}}}}}}
| 2 = {{#if:{{{hq_location_city|}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{{hq_location_city|{{{location_city|}}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{{hq_location_country|{{{location_country|}}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|{{{locations|}}}}}} | {{{num_locations|{{{locations|}}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{{areas_served|{{{area_served|}}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{net_income|{{{profit|}}}}}}{{#if:{{{net_income_year|{{{profit_year|}}}}}} | ({{{net_income_year|{{{profit_year|}}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{{owners|{{{owner|}}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}} | {{{members}}}{{#if:{{{members_year|}}} | ({{{members_year}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{{subsid|{{{subsidiaries|}}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|{{{homepage|}}}}}} |{{{website|{{{homepage|}}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}|{{{logo|{{{company_logo|}}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for clobbered parameters|check|nested=1|template=Infobox company|cat={{main other|Category:Pages using infobox company with conflicting parameters}}|name; company_name|logo; company_logo|logo_alt; alt|trade_name; trading_name|former_names; former_name|type; company_type|predecessors; predecessor|successors; successor|foundation; founded|founders; founder|defunct; dissolved|hq_location; location|hq_location_city; location_city|hq_location_country; location_country|num_locations; locations|areas_served; area_served|net_income; profit|net_income_year; profit_year|owners; owner |homepage; website
}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories }}<noinclude>
{{documentation}}
</noinclude>
7bmzzcvusd78oa0srl11hnban69qp4e
803465
803464
2025-12-01T17:58:57Z
en>Zackmann08
0
Max size for logo after [[Special:PermanentLink/1325190869|this edit]]
803465
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{#if:{{{name|}}} | {{{name}}} | {{#if:{{{company_name|}}}|{{{company_name}}}|<includeonly>{{PAGENAMEBASE}}</includeonly>}} }} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{{logo|{{{company_logo|}}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{{logo|{{{company_logo|}}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{{logo_upright|1}}} |alt={{{logo_alt|{{{alt|}}}}}} |class= {{{logo_class|}}} |maxsize=250px}}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{{image_upright|1}}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{{trade_name|{{{trading_name|}}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{{former_names|{{{former_name|}}}}}}
| label5 = Company type
| class5 = category
| data5 = {{{type|{{{company_type|}}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{predecessors|{{{predecessor|}}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{incorporated|{{{incorporation|}}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founded|{{{foundation|}}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{founders|{{{founder|}}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{defunct|{{{dissolved|}}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes |{{{successors|{{{successor|}}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}} | label | adr}}
| data17 = {{#if:{{{hq_location|{{{location|{{{headquarters|}}}}}}}}}{{{hq_location_city|{{{location_city|}}}}}}{{{hq_location_country|{{{location_country|}}}}}}
| {{Comma separated entries
| 1 = {{{hq_location|{{{location|{{{headquarters|}}}}}}}}}
| 2 = {{#if:{{{hq_location_city|}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{{hq_location_city|{{{location_city|}}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{{hq_location_country|{{{location_country|}}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|{{{locations|}}}}}} | {{{num_locations|{{{locations|}}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{{areas_served|{{{area_served|}}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{net_income|{{{profit|}}}}}}{{#if:{{{net_income_year|{{{profit_year|}}}}}} | ({{{net_income_year|{{{profit_year|}}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{{owners|{{{owner|}}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}} | {{{members}}}{{#if:{{{members_year|}}} | ({{{members_year}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{{noicon|no}}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{{subsid|{{{subsidiaries|}}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|{{{homepage|}}}}}} |{{{website|{{{homepage|}}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{{fetchwikidata|ALL}}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}|{{{logo|{{{company_logo|}}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{{logo|{{{company_logo|{{wikidata|property|raw|P154}}}}}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for clobbered parameters|check|nested=1|template=Infobox company|cat={{main other|Category:Pages using infobox company with conflicting parameters}}|name; company_name|logo; company_logo|logo_alt; alt|trade_name; trading_name|former_names; former_name|type; company_type|predecessors; predecessor|successors; successor|foundation; founded|founders; founder|defunct; dissolved|hq_location; location|hq_location_city; location_city|hq_location_country; location_country|num_locations; locations|areas_served; area_served|net_income; profit|net_income_year; profit_year|owners; owner |homepage; website
}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories }}<noinclude>
{{documentation}}
</noinclude>
th0t20p5mznpa9tg8mrzdfo3r5q6hba
803466
803465
2025-12-09T07:17:16Z
en>Zackmann08
0
Improving nested param logic using [[User:Zackmann08/unnest.js|unnest]]
803466
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{#if:{{{name|}}} | {{{name}}} | {{#if:{{{company_name|}}}|{{{company_name}}}|<includeonly>{{PAGENAMEBASE}}</includeonly>}} }} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{if empty|{{{logo|}}}|{{{company_logo|}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{if empty|{{{logo|}}}|{{{company_logo|}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{if empty|{{{logo_upright|}}}|1}} |alt={{if empty|{{{logo_alt|}}}|{{{alt|}}}}} |class= {{{logo_class|}}} |maxsize=250px}}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{if empty|{{{image_upright|}}}|1}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{if empty|{{{trade_name|}}}|{{{trading_name|}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{if empty|{{{former_names|}}}|{{{former_name|}}}}}
| label5 = Company type
| class5 = category
| data5 = {{if empty|{{{type|}}}|{{{company_type|}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{predecessors|}}}|{{{predecessor|}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{incorporated|}}}|{{{incorporation|}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founded|}}}|{{{foundation|}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founders|}}}|{{{founder|}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{defunct|}}}|{{{dissolved|}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{successors|}}}|{{{successor|}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}} | label | adr}}
| data17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}}{{{hq_location_city|}}}{{{location_city|}}}{{{hq_location_country|}}}{{{location_country|}}}
| {{Comma separated entries
| 1 = {{if empty|{{{hq_location|}}}|{{{location|}}}|{{{headquarters|}}}}}
| 2 = {{#if:{{{hq_location_city|}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{if empty|{{{hq_location_city|}}}|{{{location_city|}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{if empty|{{{hq_location_country|}}}|{{{location_country|}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|}}}{{{locations|}}} | {{if empty|{{{num_locations|}}}|{{{locations|}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{if empty|{{{areas_served|}}}|{{{area_served|}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{if empty|{{{net_income|}}}|{{{profit|}}}}}{{#if:{{{net_income_year|}}}{{{profit_year|}}} | ({{if empty|{{{net_income_year|}}}|{{{profit_year|}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{if empty|{{{owners|}}}|{{{owner|}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}} | {{{members}}}{{#if:{{{members_year|}}} | ({{{members_year}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{if empty|{{{subsid|}}}|{{{subsidiaries|}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|}}}{{{homepage|}}} |{{if empty|{{{website|}}}|{{{homepage|}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}|{{if empty|{{{logo|}}}|{{{company_logo|}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for clobbered parameters|check|nested=1|template=Infobox company|cat={{main other|Category:Pages using infobox company with conflicting parameters}}|name; company_name|logo; company_logo|logo_alt; alt|trade_name; trading_name|former_names; former_name|type; company_type|predecessors; predecessor|successors; successor|foundation; founded|founders; founder|defunct; dissolved|hq_location; location|hq_location_city; location_city|hq_location_country; location_country|num_locations; locations|areas_served; area_served|net_income; profit|net_income_year; profit_year|owners; owner |homepage; website
}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories }}<noinclude>
{{documentation}}
</noinclude>
gc9jgpi2g0wi4o68fw4obx8pl1ojmex
803467
803466
2025-12-28T03:43:16Z
en>Zackmann08
0
Module has been renamed
803467
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{#if:{{{name|}}} | {{{name}}} | {{#if:{{{company_name|}}}|{{{company_name}}}|<includeonly>{{PAGENAMEBASE}}</includeonly>}} }} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{if empty|{{{logo|}}}|{{{company_logo|}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{if empty|{{{logo|}}}|{{{company_logo|}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{if empty|{{{logo_upright|}}}|1}} |alt={{if empty|{{{logo_alt|}}}|{{{alt|}}}}} |class= {{{logo_class|}}} |maxsize=250px}}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{if empty|{{{image_upright|}}}|1}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{if empty|{{{trade_name|}}}|{{{trading_name|}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{if empty|{{{former_names|}}}|{{{former_name|}}}}}
| label5 = Company type
| class5 = category
| data5 = {{if empty|{{{type|}}}|{{{company_type|}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{predecessors|}}}|{{{predecessor|}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{incorporated|}}}|{{{incorporation|}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founded|}}}|{{{foundation|}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founders|}}}|{{{founder|}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{defunct|}}}|{{{dissolved|}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{successors|}}}|{{{successor|}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}} | label | adr}}
| data17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}}{{{hq_location_city|}}}{{{location_city|}}}{{{hq_location_country|}}}{{{location_country|}}}
| {{Comma separated entries
| 1 = {{if empty|{{{hq_location|}}}|{{{location|}}}|{{{headquarters|}}}}}
| 2 = {{#if:{{{hq_location_city|}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{if empty|{{{hq_location_city|}}}|{{{location_city|}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{if empty|{{{hq_location_country|}}}|{{{location_country|}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|}}}{{{locations|}}} | {{if empty|{{{num_locations|}}}|{{{locations|}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{if empty|{{{areas_served|}}}|{{{area_served|}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{if empty|{{{net_income|}}}|{{{profit|}}}}}{{#if:{{{net_income_year|}}}{{{profit_year|}}} | ({{if empty|{{{net_income_year|}}}|{{{profit_year|}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{if empty|{{{owners|}}}|{{{owner|}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}} | {{{members}}}{{#if:{{{members_year|}}} | ({{{members_year}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{if empty|{{{subsid|}}}|{{{subsidiaries|}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|}}}{{{homepage|}}} |{{if empty|{{{website|}}}|{{{homepage|}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}|{{if empty|{{{logo|}}}|{{{company_logo|}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for conflicting parameters|check|nested=1|template=Infobox company|cat={{main other|Category:Pages using infobox company with conflicting parameters}}|name; company_name|logo; company_logo|logo_alt; alt|trade_name; trading_name|former_names; former_name|type; company_type|predecessors; predecessor|successors; successor|foundation; founded|founders; founder|defunct; dissolved|hq_location; location|hq_location_city; location_city|hq_location_country; location_country|num_locations; locations|areas_served; area_served|net_income; profit|net_income_year; profit_year|owners; owner |homepage; website
}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories }}<noinclude>
{{documentation}}
</noinclude>
iqhh23wtr9tb2bd72v7yd37ta5ct46y
803468
803467
2026-04-22T14:24:29Z
en>Zackmann08
0
Adding [[Template:Longitem]]
803468
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{#if:{{{name|}}} | {{{name}}} | {{#if:{{{company_name|}}}|{{{company_name}}}|<includeonly>{{PAGENAMEBASE}}</includeonly>}} }} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{if empty|{{{logo|}}}|{{{company_logo|}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{if empty|{{{logo|}}}|{{{company_logo|}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{if empty|{{{logo_upright|}}}|1}} |alt={{if empty|{{{logo_alt|}}}|{{{alt|}}}}} |class= {{{logo_class|}}} |maxsize=250px}}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{if empty|{{{image_upright|}}}|1}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{if empty|{{{trade_name|}}}|{{{trading_name|}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{if empty|{{{former_names|}}}|{{{former_name|}}}}}
| label5 = {{longitem|Company type}}
| class5 = category
| data5 = {{if empty|{{{type|}}}|{{{company_type|}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{predecessors|}}}|{{{predecessor|}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{incorporated|}}}|{{{incorporation|}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founded|}}}|{{{foundation|}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founders|}}}|{{{founder|}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{defunct|}}}|{{{dissolved|}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{successors|}}}|{{{successor|}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}} | label | adr}}
| data17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}}{{{hq_location_city|}}}{{{location_city|}}}{{{hq_location_country|}}}{{{location_country|}}}
| {{Comma separated entries
| 1 = {{if empty|{{{hq_location|}}}|{{{location|}}}|{{{headquarters|}}}}}
| 2 = {{#if:{{{hq_location_city|}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{if empty|{{{hq_location_city|}}}|{{{location_city|}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{if empty|{{{hq_location_country|}}}|{{{location_country|}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|}}}{{{locations|}}} | {{if empty|{{{num_locations|}}}|{{{locations|}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{if empty|{{{areas_served|}}}|{{{area_served|}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{if empty|{{{net_income|}}}|{{{profit|}}}}}{{#if:{{{net_income_year|}}}{{{profit_year|}}} | ({{if empty|{{{net_income_year|}}}|{{{profit_year|}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{if empty|{{{owners|}}}|{{{owner|}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}} | {{{members}}}{{#if:{{{members_year|}}} | ({{{members_year}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{if empty|{{{subsid|}}}|{{{subsidiaries|}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|}}}{{{homepage|}}} |{{if empty|{{{website|}}}|{{{homepage|}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}|{{if empty|{{{logo|}}}|{{{company_logo|}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for conflicting parameters|check|nested=1|template=Infobox company|cat={{main other|Category:Pages using infobox company with conflicting parameters}}|name; company_name|logo; company_logo|logo_alt; alt|trade_name; trading_name|former_names; former_name|type; company_type|predecessors; predecessor|successors; successor|foundation; founded|founders; founder|defunct; dissolved|hq_location; location|hq_location_city; location_city|hq_location_country; location_country|num_locations; locations|areas_served; area_served|net_income; profit|net_income_year; profit_year|owners; owner |homepage; website
}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories }}<noinclude>
{{documentation}}
</noinclude>
hyra2sgzdnibcyylmqdufrx6kdo9052
803469
803468
2026-04-25T22:02:59Z
en>Zackmann08
0
Updating conflicting parameter tracking through [[:Category:Pages using infobox company with conflicting parameters]] using [[User:Zackmann08/scripts/AddCheckForConflictingParameters.js|AddCheckForConflictingParameters]] using [[Module:check for conflicting parameters]]
803469
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{if empty|{{{name|}}}|{{{company_name|}}}|<includeonly>{{PAGENAMEBASE}}</includeonly>}} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{if empty|{{{logo|}}}|{{{company_logo|}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{if empty|{{{logo|}}}|{{{company_logo|}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{if empty|{{{logo_upright|}}}|1}} |alt={{if empty|{{{logo_alt|}}}|{{{alt|}}}}} |class= {{{logo_class|}}} |maxsize=250px}}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{if empty|{{{image_upright|}}}|1}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{if empty|{{{trade_name|}}}|{{{trading_name|}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{if empty|{{{former_names|}}}|{{{former_name|}}}}}
| label5 = {{longitem|Company type}}
| class5 = category
| data5 = {{if empty|{{{type|}}}|{{{company_type|}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{predecessors|}}}|{{{predecessor|}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{incorporated|}}}|{{{incorporation|}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founded|}}}|{{{foundation|}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founders|}}}|{{{founder|}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{defunct|}}}|{{{dissolved|}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{successors|}}}|{{{successor|}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}} | label | adr}}
| data17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}}{{{hq_location_city|}}}{{{location_city|}}}{{{hq_location_country|}}}{{{location_country|}}}
| {{Comma separated entries
| 1 = {{if empty|{{{hq_location|}}}|{{{location|}}}|{{{headquarters|}}}}}
| 2 = {{#if:{{{hq_location_city|}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{if empty|{{{hq_location_city|}}}|{{{location_city|}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{if empty|{{{hq_location_country|}}}|{{{location_country|}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|}}}{{{locations|}}} | {{if empty|{{{num_locations|}}}|{{{locations|}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{if empty|{{{areas_served|}}}|{{{area_served|}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{if empty|{{{net_income|}}}|{{{profit|}}}}}{{#if:{{{net_income_year|}}}{{{profit_year|}}} | ({{if empty|{{{net_income_year|}}}|{{{profit_year|}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{if empty|{{{owners|}}}|{{{owner|}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}} | {{{members}}}{{#if:{{{members_year|}}} | ({{{members_year}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{if empty|{{{subsid|}}}|{{{subsidiaries|}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|}}}{{{homepage|}}} |{{if empty|{{{website|}}}|{{{homepage|}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}|{{if empty|{{{logo|}}}|{{{company_logo|}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories }}{{#invoke:Check for conflicting parameters|check|nested=1
| template = [[Template:Infobox company]]
| cat = {{main other|Category:Pages using infobox company with conflicting parameters}}
| logo; company_logo
| logo_alt; alt
| trade_name; trading_name
| former_names; former_name
| type; company_type
| predecessors; predecessor
| incorporated; incorporation
| founded; foundation
| founders; founder
| defunct; dissolved
| successors; successor
| hq_location; location; headquarters
| hq_location_city; location_city
| hq_location_country; location_country
| num_locations; locations
| areas_served; area_served
| net_income; profit
| net_income_year; profit_year
| owners; owner
| subsid; subsidiaries
| website; homepage
}}<noinclude>
{{documentation}}
</noinclude>
luxndm0qtjcxdvnkndyhxgz4wg5o3jn
803470
803469
2026-05-10T20:48:37Z
en>Zackmann08
0
Adding deprecated parameter tracking through [[:Category:Pages using infobox company with deprecated parameters]] per [[Template_talk:Infobox_company#Deprecated_parameter_cleanup]]
803470
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{if empty|{{{name|}}}|{{{company_name|}}}|{{PAGENAMEBASE}}}} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{if empty|{{{logo|}}}|{{{company_logo|}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{if empty|{{{logo|}}}|{{{company_logo|}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{if empty|{{{logo_upright|}}}|1}} |alt={{if empty|{{{logo_alt|}}}|{{{alt|}}}}} |class= {{{logo_class|}}} |maxsize=250px}}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{if empty|{{{image_upright|}}}|1}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{if empty|{{{trade_name|}}}|{{{trading_name|}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{if empty|{{{former_names|}}}|{{{former_name|}}}}}
| label5 = {{longitem|Company type}}
| class5 = category
| data5 = {{if empty|{{{type|}}}|{{{company_type|}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{predecessors|}}}|{{{predecessor|}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{incorporated|}}}|{{{incorporation|}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founded|}}}|{{{foundation|}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founders|}}}|{{{founder|}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{defunct|}}}|{{{dissolved|}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{successors|}}}|{{{successor|}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}} | label | adr}}
| data17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}}{{{hq_location_city|}}}{{{location_city|}}}{{{hq_location_country|}}}{{{location_country|}}}
| {{Comma separated entries
| 1 = {{if empty|{{{hq_location|}}}|{{{location|}}}|{{{headquarters|}}}}}
| 2 = {{#if:{{{hq_location_city|}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{if empty|{{{hq_location_city|}}}|{{{location_city|}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{if empty|{{{hq_location_country|}}}|{{{location_country|}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|}}}{{{locations|}}} | {{if empty|{{{num_locations|}}}|{{{locations|}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{if empty|{{{areas_served|}}}|{{{area_served|}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{if empty|{{{net_income|}}}|{{{profit|}}}}}{{#if:{{{net_income_year|}}}{{{profit_year|}}} | ({{if empty|{{{net_income_year|}}}|{{{profit_year|}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{if empty|{{{owners|}}}|{{{owner|}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}}{{{num_members|}}} | {{if empty|{{{members|}}}|{{{num_members|}}}}}{{#if:{{{members_year|}}}{{{num_members_year|}}} | ({{if empty|{{{members_year|}}}|{{{num_members_year|}}}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{if empty|{{{subsid|}}}|{{{subsidiaries|}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|}}}{{{homepage|}}} |{{if empty|{{{website|}}}|{{{homepage|}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}|{{if empty|{{{logo|}}}|{{{company_logo|}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories | num_members | num_members_year }}{{#invoke:Check for conflicting parameters|check|nested=1
| template = [[Template:Infobox company]]
| cat = {{main other|Category:Pages using infobox company with conflicting parameters}}
| logo; company_logo
| logo_alt; alt
| trade_name; trading_name
| former_names; former_name
| type; company_type
| predecessors; predecessor
| incorporated; incorporation
| founded; foundation
| founders; founder
| defunct; dissolved
| successors; successor
| hq_location; location; headquarters
| hq_location_city; location_city
| hq_location_country; location_country
| num_locations; locations
| areas_served; area_served
| net_income; profit
| net_income_year; profit_year
| owners; owner
| subsid; subsidiaries
| website; homepage
}}{{#invoke:Check for deprecated parameters|check
| _category = [[Category:Pages using infobox company with deprecated parameters|_VALUE_]]
| company_name = name
| company_logo = logo
| alt = logo_alt
| trading_name = trade_name
| company_type = type
| incorporation = incorporated
| dissolved = defunct
| location = hq_location
| headquarters = hq_location
| location_city = hq_location_city
| location_country = hq_location_country
| locations = num_locations
| members = num_members
| members_year = num_members_year
| profit = net_income
| profit_year = net_income_year
| subsid = subsidiaries
| foundation = founded
| homepage = website
}}<noinclude>
{{documentation}}
</noinclude>
sjbgzgx1abke1n3ihdcy0nc7i5rk7o1
803471
803470
2026-05-10T21:38:21Z
en>Zackmann08
0
Clarify categorization of non-articles
803471
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{if empty|{{{name|}}}|{{{company_name|}}}|{{PAGENAMEBASE}}}} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{if empty|{{{logo|}}}|{{{company_logo|}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{if empty|{{{logo|}}}|{{{company_logo|}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{if empty|{{{logo_upright|}}}|1}} |alt={{if empty|{{{logo_alt|}}}|{{{alt|}}}}} |class= {{{logo_class|}}} |maxsize=250px}}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{if empty|{{{image_upright|}}}|1}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{if empty|{{{trade_name|}}}|{{{trading_name|}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{if empty|{{{former_names|}}}|{{{former_name|}}}}}
| label5 = {{longitem|Company type}}
| class5 = category
| data5 = {{if empty|{{{type|}}}|{{{company_type|}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{predecessors|}}}|{{{predecessor|}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{incorporated|}}}|{{{incorporation|}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founded|}}}|{{{foundation|}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founders|}}}|{{{founder|}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{defunct|}}}|{{{dissolved|}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{successors|}}}|{{{successor|}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}} | label | adr}}
| data17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}}{{{hq_location_city|}}}{{{location_city|}}}{{{hq_location_country|}}}{{{location_country|}}}
| {{Comma separated entries
| 1 = {{if empty|{{{hq_location|}}}|{{{location|}}}|{{{headquarters|}}}}}
| 2 = {{#if:{{{hq_location_city|}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{if empty|{{{hq_location_city|}}}|{{{location_city|}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{if empty|{{{hq_location_country|}}}|{{{location_country|}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|}}}{{{locations|}}} | {{if empty|{{{num_locations|}}}|{{{locations|}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{if empty|{{{areas_served|}}}|{{{area_served|}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{if empty|{{{net_income|}}}|{{{profit|}}}}}{{#if:{{{net_income_year|}}}{{{profit_year|}}} | ({{if empty|{{{net_income_year|}}}|{{{profit_year|}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{if empty|{{{owners|}}}|{{{owner|}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}}{{{num_members|}}} | {{if empty|{{{members|}}}|{{{num_members|}}}}}{{#if:{{{members_year|}}}{{{num_members_year|}}} | ({{if empty|{{{members_year|}}}|{{{num_members_year|}}}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{if empty|{{{subsid|}}}|{{{subsidiaries|}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|}}}{{{homepage|}}} |{{if empty|{{{website|}}}|{{{homepage|}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}|{{if empty|{{{logo|}}}|{{{company_logo|}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories | num_members | num_members_year }}{{#invoke:Check for conflicting parameters|check|nested=1
| template = [[Template:Infobox company]]
| cat = {{main other|Category:Pages using infobox company with conflicting parameters}}
| logo; company_logo
| logo_alt; alt
| trade_name; trading_name
| former_names; former_name
| type; company_type
| predecessors; predecessor
| incorporated; incorporation
| founded; foundation
| founders; founder
| defunct; dissolved
| successors; successor
| hq_location; location; headquarters
| hq_location_city; location_city
| hq_location_country; location_country
| num_locations; locations
| areas_served; area_served
| net_income; profit
| net_income_year; profit_year
| owners; owner
| subsid; subsidiaries
| website; homepage
}}{{#invoke:Check for deprecated parameters|check
| _category = {{main other|[[Category:Pages using infobox company with deprecated parameters|_VALUE_]]|[[Category:Pages using infobox company with deprecated parameters|§]]}}
| company_name = name
| company_logo = logo
| alt = logo_alt
| trading_name = trade_name
| company_type = type
| incorporation = incorporated
| dissolved = defunct
| location = hq_location
| headquarters = hq_location
| location_city = hq_location_city
| location_country = hq_location_country
| locations = num_locations
| members = num_members
| members_year = num_members_year
| profit = net_income
| profit_year = net_income_year
| subsid = subsidiaries
| foundation = founded
| homepage = website
}}<noinclude>
{{documentation}}
</noinclude>
2zgnaa0ljrq224xd8nhfre8787gnylq
803472
803471
2026-05-23T06:20:15Z
en>Zackmann08
0
No need to include all these user/draft articles. I regret this decision..
803472
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{if empty|{{{name|}}}|{{{company_name|}}}|{{PAGENAMEBASE}}}} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{if empty|{{{logo|}}}|{{{company_logo|}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{if empty|{{{logo|}}}|{{{company_logo|}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{if empty|{{{logo_upright|}}}|1}} |alt={{if empty|{{{logo_alt|}}}|{{{alt|}}}}} |class= {{{logo_class|}}} |maxsize=250px}}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{if empty|{{{image_upright|}}}|1}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{if empty|{{{trade_name|}}}|{{{trading_name|}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{if empty|{{{former_names|}}}|{{{former_name|}}}}}
| label5 = {{longitem|Company type}}
| class5 = category
| data5 = {{if empty|{{{type|}}}|{{{company_type|}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{predecessors|}}}|{{{predecessor|}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{incorporated|}}}|{{{incorporation|}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founded|}}}|{{{foundation|}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founders|}}}|{{{founder|}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{defunct|}}}|{{{dissolved|}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{successors|}}}|{{{successor|}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}} | label | adr}}
| data17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}}{{{hq_location_city|}}}{{{location_city|}}}{{{hq_location_country|}}}{{{location_country|}}}
| {{Comma separated entries
| 1 = {{if empty|{{{hq_location|}}}|{{{location|}}}|{{{headquarters|}}}}}
| 2 = {{#if:{{{hq_location_city|}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{if empty|{{{hq_location_city|}}}|{{{location_city|}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{if empty|{{{hq_location_country|}}}|{{{location_country|}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|}}}{{{locations|}}} | {{if empty|{{{num_locations|}}}|{{{locations|}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{if empty|{{{areas_served|}}}|{{{area_served|}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{if empty|{{{net_income|}}}|{{{profit|}}}}}{{#if:{{{net_income_year|}}}{{{profit_year|}}} | ({{if empty|{{{net_income_year|}}}|{{{profit_year|}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{if empty|{{{owners|}}}|{{{owner|}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}}{{{num_members|}}} | {{if empty|{{{members|}}}|{{{num_members|}}}}}{{#if:{{{members_year|}}}{{{num_members_year|}}} | ({{if empty|{{{members_year|}}}|{{{num_members_year|}}}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{if empty|{{{subsid|}}}|{{{subsidiaries|}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|}}}{{{homepage|}}} |{{if empty|{{{website|}}}|{{{homepage|}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#if:{{{trading_name|}}}|[[Category:Pages using infobox company using trading name]]
}}{{#ifeq:{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}|{{if empty|{{{logo|}}}|{{{company_logo|}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories | num_members | num_members_year }}{{#invoke:Check for conflicting parameters|check|nested=1
| template = [[Template:Infobox company]]
| cat = {{main other|Category:Pages using infobox company with conflicting parameters}}
| logo; company_logo
| logo_alt; alt
| trade_name; trading_name
| former_names; former_name
| type; company_type
| predecessors; predecessor
| incorporated; incorporation
| founded; foundation
| founders; founder
| defunct; dissolved
| successors; successor
| hq_location; location; headquarters
| hq_location_city; location_city
| hq_location_country; location_country
| num_locations; locations
| areas_served; area_served
| net_income; profit
| net_income_year; profit_year
| owners; owner
| subsid; subsidiaries
| website; homepage
}}{{#invoke:Check for deprecated parameters|check
| _category = {{main other|[[Category:Pages using infobox company with deprecated parameters|_VALUE_]]}}
| company_name = name
| company_logo = logo
| alt = logo_alt
| trading_name = trade_name
| company_type = type
| incorporation = incorporated
| dissolved = defunct
| location = hq_location
| headquarters = hq_location
| location_city = hq_location_city
| location_country = hq_location_country
| locations = num_locations
| members = num_members
| members_year = num_members_year
| profit = net_income
| profit_year = net_income_year
| subsid = subsidiaries
| foundation = founded
| homepage = website
}}<noinclude>
{{documentation}}
</noinclude>
2m9aylyfabfoupjov39hsx1mo4u65f4
803473
803472
2026-06-15T22:33:45Z
en>Zackmann08
0
cat not needed. This is being dealt with in the deprecation cleanup
803473
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{if empty|{{{name|}}}|{{{company_name|}}}|{{PAGENAMEBASE}}}} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{if empty|{{{logo|}}}|{{{company_logo|}}}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{if empty|{{{logo|}}}|{{{company_logo|}}}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{if empty|{{{logo_upright|}}}|1}} |alt={{if empty|{{{logo_alt|}}}|{{{alt|}}}}} |class= {{{logo_class|}}} |maxsize=250px}}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{if empty|{{{image_upright|}}}|1}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{if empty|{{{trade_name|}}}|{{{trading_name|}}}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{if empty|{{{former_names|}}}|{{{former_name|}}}}}
| label5 = {{longitem|Company type}}
| class5 = category
| data5 = {{if empty|{{{type|}}}|{{{company_type|}}}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{predecessors|}}}|{{{predecessor|}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{incorporated|}}}|{{{incorporation|}}}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founded|}}}|{{{foundation|}}}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founders|}}}|{{{founder|}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{defunct|}}}|{{{dissolved|}}}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{successors|}}}|{{{successor|}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}} | label | adr}}
| data17 = {{#if:{{{hq_location|}}}{{{location|}}}{{{headquarters|}}}{{{hq_location_city|}}}{{{location_city|}}}{{{hq_location_country|}}}{{{location_country|}}}
| {{Comma separated entries
| 1 = {{if empty|{{{hq_location|}}}|{{{location|}}}|{{{headquarters|}}}}}
| 2 = {{#if:{{{hq_location_city|}}}{{{location_city|}}} |<div class="ib-company-locality locality">{{#if:{{{hq_location_city|}}}{{{location_city|}}} | {{if empty|{{{hq_location_city|}}}|{{{location_city|}}}}} | }}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}{{{location_country|}}} |<div class="ib-company-country country-name">{{#if:{{{hq_location_country|}}}{{{location_country|}}} | {{if empty|{{{hq_location_country|}}}|{{{location_country|}}}}} | }}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{#if:{{{num_locations|}}}{{{locations|}}} | {{if empty|{{{num_locations|}}}|{{{locations|}}}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }} }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{if empty|{{{areas_served|}}}|{{{area_served|}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{if empty|{{{net_income|}}}|{{{profit|}}}}}{{#if:{{{net_income_year|}}}{{{profit_year|}}} | ({{if empty|{{{net_income_year|}}}|{{{profit_year|}}}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{if empty|{{{owners|}}}|{{{owner|}}}}}
| label33 = Members
| data33 = {{#if:{{{members|}}}{{{num_members|}}} | {{if empty|{{{members|}}}|{{{num_members|}}}}}{{#if:{{{members_year|}}}{{{num_members_year|}}} | ({{if empty|{{{members_year|}}}|{{{num_members_year|}}}}})}} }}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{if empty|{{{subsid|}}}|{{{subsidiaries|}}}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|}}}{{{homepage|}}} |{{if empty|{{{website|}}}|{{{homepage|}}}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes{{\}}references'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#ifeq:{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}|{{if empty|{{{logo|}}}|{{{company_logo|}}}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{if empty|{{{logo|}}}|{{{company_logo|}}}|{{wikidata|property|raw|P154}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_" | ignoreblank=y | alt | area_served | areas_served | assets | assets_year | aum | brands | company_logo | company_name | company_type | defunct | dissolved | divisions | embed | equity | equity_year | fate | footnotes | headquarters | former_name | former_names | foundation | founded | founder | founders | genre | homepage | hq_location | hq_location_city | hq_location_country | incorporated | image | image_alt | image_caption | image_size | image_upright | income_year | industry | ISIN | key_people | location | location_city | location_country | locations | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | members | members_year | module | name | native_name | native_name_lang | net_income | net_income_year | num_employees | num_employees_year | num_locations | num_locations_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | profit | profit_year | rating | ratio | revenue | revenue_year | romanized_name | services | subsid | subsidiaries | successor | successors | traded_as | trade_name | trading_name | type | website| qid | fetchwikidata | suppressfields | noicon | nocat | demo | categories | num_members | num_members_year }}{{#invoke:Check for conflicting parameters|check|nested=1
| template = [[Template:Infobox company]]
| cat = {{main other|Category:Pages using infobox company with conflicting parameters}}
| logo; company_logo
| logo_alt; alt
| trade_name; trading_name
| former_names; former_name
| type; company_type
| predecessors; predecessor
| incorporated; incorporation
| founded; foundation
| founders; founder
| defunct; dissolved
| successors; successor
| hq_location; location; headquarters
| hq_location_city; location_city
| hq_location_country; location_country
| num_locations; locations
| areas_served; area_served
| net_income; profit
| net_income_year; profit_year
| owners; owner
| subsid; subsidiaries
| website; homepage
}}{{#invoke:Check for deprecated parameters|check
| _category = {{main other|[[Category:Pages using infobox company with deprecated parameters|_VALUE_]]}}
| company_name = name
| company_logo = logo
| alt = logo_alt
| trading_name = trade_name
| company_type = type
| incorporation = incorporated
| dissolved = defunct
| location = hq_location
| headquarters = hq_location
| location_city = hq_location_city
| location_country = hq_location_country
| locations = num_locations
| members = num_members
| members_year = num_members_year
| profit = net_income
| profit_year = net_income_year
| subsid = subsidiaries
| foundation = founded
| homepage = website
}}<noinclude>
{{documentation}}
</noinclude>
2epeqyg1ciypk7z3e1fpjm5z0tzce9b
803474
803473
2026-06-16T21:34:52Z
en>Zackmann08
0
removing support for deprecated parameters that have been cleaned.
803474
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{if empty|{{{name|}}}|{{PAGENAMEBASE}}}} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{{logo|}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{{logo|}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{if empty|{{{logo_upright|}}}|1}} |alt={{{logo_alt|}}} |class= {{{logo_class|}}} |maxsize=250px}}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{if empty|{{{image_upright|}}}|1}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{{trade_name|}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{if empty|{{{former_names|}}}|{{{former_name|}}}}}
| label5 = Type
| class5 = category
| data5 = {{{type|}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{predecessors|}}}|{{{predecessor|}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{incorporated|}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes | {{{founded|}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founders|}}}|{{{founder|}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{defunct|}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{successors|}}}|{{{successor|}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|}}} | label | adr}}
| data17 = {{#if:{{{hq_location|}}}{{{hq_location_city|}}}{{{hq_location_country|}}}
| {{Comma separated entries
| 1 = {{{hq_location|}}}
| 2 = {{#if:{{{hq_location_city|}}} |<div class="ib-company-locality locality"> {{{hq_location_city|}}}</div>}}
| 3 = {{#if:{{{hq_location_country|}}}} |<div class="ib-company-country country-name"> {{{hq_location_country|}}}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{{num_locations|}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{if empty|{{{areas_served|}}}|{{{area_served|}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{net_income|}}}{{#if:{{{net_income_year|}}} | ({{{net_income_year|}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{if empty|{{{owners|}}}|{{{owner|}}}}}
| label33 = Members
| data33 = {{{num_members|}}}{{#if:{{{num_members_year|}}} | ({{{num_members_year|}}})}}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{{subsidiaries|}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|}}}| {{{website|}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#ifeq:{{if empty|{{{logo|}}}|{{wikidata|property|raw|P154}}}}|{{{logo|}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{if empty|{{{logo|}}}|{{wikidata|property|raw|P154}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_"|ignoreblank=y| area_served | areas_served | assets | assets_year | aum | brands | defunct | divisions | embed | equity | equity_year | fate | fetchwikidata | footnotes | former_name | former_names | founded | founder | founders | genre | hq_location | hq_location_city | hq_location_country | image | image_alt | image_caption | image_size | image_upright | income_year | incorporated | industry | ISIN | key_people | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | module | name | native_name | native_name_lang | net_income | net_income_year | noicon | num_employees | num_employees_year | num_locations | num_locations_year | num_members | num_members_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | qid | rating | ratio | revenue | revenue_year | romanized_name | services | subsidiaries | successor | successors | suppressfields | trade_name | traded_as | type | website }}{{#invoke:Check for conflicting parameters|check
| template = [[Template:Infobox company]]
| cat = {{main other|Category:Pages using infobox company with conflicting parameters}}
| former_names; former_name
| predecessors; predecessor
| founders; founder
| successors; successor
| areas_served; area_served
| owners; owner
}}<noinclude>
{{documentation}}
</noinclude>
1qg74qt2zwapm7b4zvv5wux8vr6ww6l
803475
803474
2026-06-25T11:34:46Z
en>Primefac
0
rmv excess }
803475
wikitext
text/x-wiki
{{Infobox
| bodyclass = ib-company vcard
| templatestyles = Infobox company/styles.css
| child = {{lc:{{{embed}}}}}
| decat = yes
| titleclass = fn org
| titlestyle = font-size: 125%;
| title = {{#ifeq:{{lc:{{{embed}}}}} | yes | '''Company''' | {{if empty|{{{name|}}}|{{PAGENAMEBASE}}}} }}
| imageclass = ib-company-logo logo
| image = {{#invoke:InfoboxImage |InfoboxImage |image={{#ifeq:{{lc:{{{embed}}}}} | yes | {{{logo|}}} |{{#invoke:WikidataIB |getValue |rank=best |P154 |name=logo |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=no |noicon=yes |maxvals=1 |{{{logo|}}} }} }} |size={{{logo_size|}}} |sizedefault=frameless |upright={{if empty|{{{logo_upright|}}}|1}} |alt={{{logo_alt|}}} |class= {{{logo_class|}}} |maxsize=250px}}
| caption = {{{logo_caption|}}}
| image2 = {{#invoke:InfoboxImage |InfoboxImage |image={{{image|}}} |size={{{image_size|}}} |upright={{if empty|{{{image_upright|}}}|1}} |alt={{{image_alt|}}} }}
| caption2 = {{{image_caption|}}}
| label1 = {{longitem|[[Trade name]]}}
| data1 = {{{trade_name|}}}
| label2 = {{longitem|Native name}}
| data2 = {{#if:{{{native_name|}}} | {{#if:{{{native_name_lang|}}} | {{lang|{{{native_name_lang}}}|{{{native_name}}} }} | {{{native_name}}} }} }}
| label3 = {{longitem|[[Romanization|Romanized]] name}}
| data3 = {{{romanized_name|}}}
| label4 = Formerly
| class4 = nickname
| data4 = {{if empty|{{{former_names|}}}|{{{former_name|}}}}}
| label5 = Type
| class5 = category
| data5 = {{{type|}}}
| label6 = {{longitem|[[Ticker symbol|Traded as]]}}
| data6 = {{{traded_as|}}}
| label7 = [[International Securities Identification Number|ISIN]]
| data7 = {{#invoke:WikidataIB |getValue |rank=best |P946 |name=ISIN |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{ISIN|}}} }}
| label8 = Industry
| class8 = category
| data8 = {{#invoke:WikidataIB |getValue |rank=best |P452 |name=industry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{industry|}}} }}
| label9 = Genre
| class9 = category
| data9 = {{{genre|}}}
| label10 = Predecessor{{#if:{{{predecessors|}}}|s}}
| data10 = {{#invoke:WikidataIB |getValue |rank=best |P155 |name=predecessor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{predecessors|}}}|{{{predecessor|}}}}} }}
| label11 = Incorporated
| data11 = {{#invoke:WikidataIB |getValue |rank=best |P1001 |name=incorporated |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{incorporated|}}} }}
| label12 = Founded
| data12 = {{#invoke:WikidataIB |getValue |rank=best |P571 |name=founded |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes | {{{founded|}}} }}
| label13 = Founder{{#if:{{{founders|}}}|s}}
| class13 = agent
| data13 = {{#invoke:WikidataIB |getValue |rank=best |P112 |name=founder |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{founders|}}}|{{{founder|}}}}} }}
| label14 = Defunct
| data14 = {{#invoke:WikidataIB |getValue |rank=best |P576 |name=defunct |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{{defunct|}}} }}
| label15 = Fate
| data15 = {{{fate|}}}
| label16 = Successor{{#if:{{{successors|}}}|s}}
| data16 = {{#invoke:WikidataIB |getValue |rank=best |P156 |name=successor |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes |{{if empty|{{{successors|}}}|{{{successor|}}}}} }}
| label17 = Headquarters
| class17 = {{#if:{{{hq_location|}}} | label | adr}}
| data17 = {{#if:{{{hq_location|}}}{{{hq_location_city|}}}{{{hq_location_country|}}}
| {{Comma separated entries
| 1 = {{{hq_location|}}}
| 2 = {{#if:{{{hq_location_city|}}} |<div class="ib-company-locality locality"> {{{hq_location_city|}}}</div>}}
| 3 = {{#if:{{{hq_location_country|}}} |<div class="ib-company-country country-name"> {{{hq_location_country|}}}</div>}}
}}
| {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}
| {{Comma separated entries
| 1 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P159 |name=hq_location_city |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-locality locality">{{#invoke:WikidataIB |getValue |rank=best |P159 |name=headquarters location |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
| 2 = {{#if:{{#invoke:WikidataIB |getValue |rank=best |P17 |name=countrry |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }} |<div class="ib-company-country country-name">{{#invoke:WikidataIB |getValue |rank=best |P17 |name=hq_location_country |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 }}</div>}}
}}
}}
}}
| label19 = {{longitem|Number of locations}}
| data19 = {{{num_locations|}}}{{#if:{{{num_locations_year|}}} | ({{{num_locations_year}}}) }}
| label20 = {{longitem|Area{{#if:{{{areas_served|}}}|s}} served}}
| data20 = {{if empty|{{{areas_served|}}}|{{{area_served|}}}}}
| label21 = {{longitem|Key people}}
| class21 = agent
| data21 = {{{key_people|}}}
| label22 = Products
| data22 = {{{products|}}}
| label23 = {{longitem|Production output}}
| data23 = {{#if:{{{production|}}} | {{{production|}}}{{#if:{{{production_year|}}} | ({{{production_year}}}) }} }}
| label24 = Brands
| data24 = {{{brands|}}}
| label25 = Services
| class25 = category
| data25 = {{{services|}}}
| label26 = Revenue
| data26 = {{#invoke:WikidataIB |getValue |rank=best |P2139 |name=revenue |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{revenue|}}}{{#if:{{{revenue_year|}}} | ({{{revenue_year}}}) }} }}
| label27 = {{longitem|[[Earnings before interest and taxes|Operating income]]}}
| data27 = {{#invoke:WikidataIB |getValue |rank=best |P3362 |name=operating_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{operating_income|}}}{{#if:{{{income_year|}}} | ({{{income_year}}}) }} }}
| label28 = {{longitem|[[Net income]]}}
| data28 = {{#invoke:WikidataIB |getValue |rank=best |P2295 |name=net_income |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{net_income|}}}{{#if:{{{net_income_year|}}} | ({{{net_income_year|}}}) }} }}
| label29 = [[Assets under management|AUM]]
| data29 = {{{aum|}}}
| label30 = {{nowrap|[[Asset|Total assets]]}}
| data30 = {{#invoke:WikidataIB |getValue |rank=best |P2403 |name=assets |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{assets|}}}{{#if:{{{assets_year|}}} | ({{{assets_year}}}) }} }}
| label31 = {{nowrap|[[Equity (finance)|Total equity]]}}
| data31 = {{#if:{{{equity|}}} | {{{equity}}}{{#if:{{{equity_year|}}} | ({{{equity_year}}})}} }}
| label32 = Owner{{#if:{{{owners|}}}|s}}
| data32 = {{if empty|{{{owners|}}}|{{{owner|}}}}}
| label33 = Members
| data33 = {{{num_members|}}}{{#if:{{{num_members_year|}}} | ({{{num_members_year|}}})}}
| label34 = {{longitem|Number of employees}}
| data34 = {{#invoke:WikidataIB |getValue |rank=best |P1128 |name=num_employees |qual=P585 |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |maxvals=1 |{{{num_employees|}}}{{#if:{{{num_employees_year|}}} | ({{{num_employees_year}}}) }} }}
| label35 = [[Parent company|Parent]]
| data35 = {{#ifeq:{{{owners|}}}{{{owner|}}}{{{parent|}}} || {{#invoke:WikidataIB |getValue |rank=best |P749 |name=parent |qid={{{qid|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |suppressfields={{{suppressfields|}}} |onlysourced=yes |noicon={{if empty|{{{noicon|}}}|no}} |sep="<br />" |sorted=yes }} | {{{parent|}}} }}
| label36 = [[Division (business)|Divisions]]
| data36 = {{{divisions|}}}
| label37 = [[Subsidiary|Subsidiaries]]
| data37 = {{{subsidiaries|}}}
| data39 = {{{module|}}}
| label40 = [[Basel III|Capital ratio]]
| data40 = {{{ratio|}}}
| label41 = Rating
| data41 = {{{rating|}}}
| label42 = Website
| data42 = {{#if:{{{website|}}}| {{{website|}}} |{{#invoke:WikidataIB |url2 |url={{#invoke:WikidataIB |getValue |rank=best |P856 |name=website |qid={{{qid|}}} |suppressfields={{{suppressfields|}}} |fetchwikidata={{if empty|{{{fetchwikidata|}}}|ALL}} |onlysourced=yes |maxvals=1}} }} }}
| below = {{#if:{{{footnotes|}}} | '''Footnotes'''{{break}}{{{footnotes}}} }}
}}<!-- Tracking categories:
-->{{main other|{{#ifeq:{{if empty|{{{logo|}}}|{{wikidata|property|raw|P154}}}}|{{{logo|}}}||[[Category:Pages using infobox company with a logo from wikidata]]
}}{{#if:{{{image|}}}|{{#ifeq:{{#invoke:string|replace|{{{image|}}}| |_}}|{{#invoke:string|replace|{{if empty|{{{logo|}}}|{{wikidata|property|raw|P154}}}}| |_}}|[[Category:Pages using infobox company with a duplicate image]]|}}|}}
}}<!--
-->{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox company with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox company]] with unknown parameter "_VALUE_"|ignoreblank=y| area_served | areas_served | assets | assets_year | aum | brands | defunct | divisions | embed | equity | equity_year | fate | fetchwikidata | footnotes | former_name | former_names | founded | founder | founders | genre | hq_location | hq_location_city | hq_location_country | image | image_alt | image_caption | image_size | image_upright | income_year | incorporated | industry | ISIN | key_people | logo | logo_alt | logo_caption | logo_class | logo_size | logo_upright | module | name | native_name | native_name_lang | net_income | net_income_year | noicon | num_employees | num_employees_year | num_locations | num_locations_year | num_members | num_members_year | operating_income | owner | owners | parent | predecessor | predecessors | production | production_year | products | qid | rating | ratio | revenue | revenue_year | romanized_name | services | subsidiaries | successor | successors | suppressfields | trade_name | traded_as | type | website }}{{#invoke:Check for conflicting parameters|check
| template = [[Template:Infobox company]]
| cat = {{main other|Category:Pages using infobox company with conflicting parameters}}
| former_names; former_name
| predecessors; predecessor
| founders; founder
| successors; successor
| areas_served; area_served
| owners; owner
}}<noinclude>
{{documentation}}
</noinclude>
h0ejwsqmtky2x3w9jjx4hh62mt5l9bp
अशोक
0
28560
803313
765801
2026-07-29T16:20:12Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803313
wikitext
text/x-wiki
{{Infobox royalty
|title = चक्रवर्ती <ref name="Fogelin2015">{{cite book|author=Lars Fogelin|title=An Archaeological History of Indian Buddhism|url=https://books.google.com/books?id=yPZzBgAAQBAJ&pg=PA81|date=1 अप्रैल 2015|publisher=Oxford University Press|isbn=978-0-19-994823-9|pages=81–}}</ref><ref name="Kleiner2015">{{cite book|author=Fred Kleiner|title=Gardner’s Art through the Ages: A Global History|url=https://books.google.com/books?id=q4bCBAAAQBAJ&pg=PT474|date=1 जनवरी 2015|publisher=Cengage Learning|isbn=978-1-305-54484-0|pages=474–}}</ref>
|image =Indian_relief_from_Amaravati,_Guntur._Preserved_in_Guimet_Museum.jpg
|caption = [[अमरावती गाँव, गुंटूर जिला|अमरावती]], [[आंध्रप्रदेश]] (भारत) में लगभग 1ली सदी ईपू/ईसवी के एगो चित्र, बीच में शाईट अशोक के देखावल गइल बाटे।
|succession= तीसरका मौर्य सम्राट
|reign = 268 ईपू — ल॰ 232 ईपू{{sfn|Upinder Singh|2008|p=331}}
|coronation = 268 ईपू{{sfn|Upinder Singh|2008|p=331}}
|predecessor = [[बिंदुसार]]
|successor = [[दशरथ मौर्य|दशरथ]]
|othertitles= ''[[देवानंपिय]]'', ''[[पियदसी]]''
|spouse=[[असंधिमित्र]]
|spouse-type=साथी
|spouses={{unbulleted list|[[देवी (असोक के मेहरारू)|देवी]]
|[[करुवाकी]]
|[[रानी पद्मावती|पद्मावती]]
|[[तिष्यारक्षा]]}}
|issue={{unbulleted list|[[महिंद]]
|[[संघमित्ता]]
|[[तिवाला]]
|[[कुनाल]]
|[[चारुमती]]}}
|spouses-type =पत्नी
|dynasty = [[मौर्य साम्राज्य|मौर्य]]
|father = [[बिंदुसार]]
|mother = [[सुभद्रांगी]]
|birth_date = 304 ईपू
|birth_place = [[पाटलिपुत्र]], [[पटना]]
|religion = <!-- Do not add anything here. (See talk.) The reader can read about Ashoka's religion in the main article. !-->
|death_date = 232 ईपू (उमिर 72)
|death_place = [[पाटलिपुत्र]], [[पटना]]
}}
{{Maurya}}
'''अशोक''' (304—232 ईपू),<ref>{{cite web|last=Chandra |first=Amulya |url=http://www.britannica.com/biography/Ashoka |title=Ashoka | biography - emperor of India |publisher=Britannica.com |date=2015-05-14 |accessdate=2015-08-09}}</ref> चाहे '''असोक महान''' [[मौर्य वंश]] के एगो भारतीय राजा रहलें जे {{circa|268}} से 232 ईपू ले लगभग मय भारत पऽ राज कइलें।<ref>Thapur (1973), p. 51.</ref> भारत के महान राजा लोग मे से एगो, असोक के राज उत्तर-पच्छिम मे हिंदुकुश से ले के पूरुब मे आज के बांग्लादेश ले, आ दक्खिन के ओर आजकाल के तमिलनाडु आ केरल के कुछ हिस्सा छोड़ के बाकी मय भारत के छेंकले रहे। एह राज के राजधानी [[मगध]] मे [[पाटलिपुत्र]] (वर्तमान [[पटना]]) रहल आ [[उज्जैन]] आ तक्षशिला में क्षेत्रीय राजधानी रहलीं सऽ।
[[चित्र:Maurya empire in 265 BCE.jpg|thumb|left|320px|सम्राट अशोक के समय में [[मौर्य साम्राज्य]]]]
अशोक के जीवन के सभसे बड़ घटना [[कलिंग के लड़ाई]] रहल जेकरे बाद ऊ लड़ाई आ हत्या के तेज के [[बौद्ध धरम]] के मान लिहलें आ सांति के पसार मे लाग गइलें। अशोक के घड़ी मे [[तीसरी बौद्ध संगीति|तीसरका बौद्ध संगीति]] पाटलिपुत्र मे भइल आ ऊ भारत से बाहर के देस सभ में बुद्ध के उपदेस पसारे ला धम्म के पसार करे ला लोग भेजलें।
अशोक कय जगह प खम्भा गड़ववलें जिनहन प नैतिक उपदेश खोदवावल रहल। इन्हन के आज [[अशोक स्तंभ]] कहल जाला। भारत के राष्ट्रीय चीन्हा [[सारनाथ]] के अशोक स्तंभ से लिहल गइल बाटे।
== नाँव ==
[[अशोक के आदेशलेख|आदेशलेख]] सभ में '''अशोक''' नाँव दू बेर आइल बा, पहिला मास्की के लेख में जेने सुरूआते ''देवनांपियस अशोकस'' से भइल बा, आ दुसरा बेर गुर्जरा के अभिलेख में अशोक नाँव आइल बा। अबले के अनुमान से इहे साबित भइल बा की बाकी आदेशलेखन में जेकरा के '''पियदसि''' आ ''देवानांपिय'' कहल गइल बा ऊ उहे राजा हवें जिनके बौद्ध ग्रंथन में '''अशोक''' बतावल गइल बा आ पुराणन में '''अशोकवर्द्धन''' कहल गइल बा।<ref name="Shastri2007" /> हालाँकि, नीलकंठ शास्त्री के कहनाम बा कि पियदसि इनकर असली नाँव रहल आ अशोक {{abbr|विरुद|आदर में दिहल नाँव}}, या फिर अशोक असली नाँव रहल आ पियदसि विरुद रहल, ई बात तयजिवन जिइल कठिन बा<ref name="Shastri2007">{{cite book|author=के ए नीलकंठ शास्त्री|title=नंद-मौर्य युगीन भारत|url=https://books.google.com/books?id=T85aSmU7BGoC&pg=PA233|year=2007|publisher=मोतीलाल बनारसीदास|isbn=978-81-208-2104-0|pages=233–}}</ref>
== जीवन ==
'''अशोक के शुरुआती जीवन'''
चक्रवर्ती अशोक सम्राट [[बिंदुसार]] आउर रानी धम्मा के बेटा रहन।<ref>https://en.m.wikipedia.org/wiki/Ashoka#CITEREFStrong1989</ref> लंका के परम्परा में बिंदुसार के सोरह पटरानियन आउर 101 बेटन के उल्लेख बा। बेटन में खाली तीन गो के नाँव लिखल बा, [[सूसिम]] जवन सभसे बड़ रहन, अशोक आऊर तिष्य। तिष्य अशोक के सहोदरदभाई आऊर सभसे छोट रहन ।एक दिन ध्म्मा के सपना आइल जे उनकर बेटा बहूत बड़ सम्राट बनी। ओकरा बादे से राजा बिन्दुसार ओकरा आपन रानी बना लिहलन बाकिर धर्मा क्षत्रिय कुल के ना रहे, तऽ ओकरा कवनो बिशेस स्थान ना मिलल रहे। अशोक के कय गो (सौतेला) भाई — बहिन रहऽसन आउर ओकनी मे कड़ा प्रतिस्पर्धा रहत रहे । अशोक के घड़ी के ऊकेरल गइल प्रतीतात्मक चिह्न जेकरा हमनी के '[[अशोक चिह्न]]' के नाँव से जानेनी जा आज उ भारत के राष्ट्रीय चिह्न हऽ। [[बौद्ध धर्म]] के इतिहास में [[गौतम बुद्ध]] के बाद अशोके के नाँव आवेला।
दिव्यदान में अशोक के एगो मेहरारु के नाँव 'तिष्यरक्षिता' मिलेला। ओकर लेखा में खाली ओकर मेहरारु 'करूणावकि' बिया। दिव्यादान में अशोक के दु भाइयन सुसीम आ विगताशोक के नाम लिखल बड़ुवे।
अशोक मौर्य सम्राट [[चंद्रगुप्त मौर्य]] के पोता रहन। रोमन इतिहासकार अपियन के मोताबिक, चन्द्रगुप्त [[सैल्युकस]] के बेटी से बियाह कइले रहे; एही चलते इहो ह़ो सकेला कि अशोक के आजी युनानी होइहेँ।<ref>https://books.google.fr/books?id=sMoLhNQ9KRoC&pg=PA393#v=onepage&q&f=false</ref><ref>[https://books.google.fr/books?id=Kwnv3I6qIosC&pg=PA86 द] ब्रीफ हिस्टरी ऑफ इंडिया
</ref> भारतीय ग्रंथ [[भविष्य पुराण]] में भी एह बात के लिखल गइल बा कि चंद्रगुप्त के बियाह सैल्युकस के बेटी से भइल रहे।<ref>फौरन इन्फ्लुएंस ऑन इंडिया, कृष्णा चन्द्र सागर,[https://books.google.co.in/books?id=0UA4rkm9MgkC&pg=PA83&redir_esc=y#v=onepage&q&f=false 1992]</ref>
पुरान बुद्ध, जैन आ हिन्दु लेखन से अशोक के बारे में अलग-अलग बात पता चलेला। [[अवदान साहित्य]] मे लिखल बा कि अशोक रानी सुभद्रंगी के बेटा रहन। अशोकवदन में लिखल बा कि अशोक के माई चम्पा नगर के एगो ब्राह्मण के बेटी रही।<ref>http://www.tandfonline.com/doi/abs/10.1080/02666030.2012.725581</ref><ref>रेलिक्स ऑफ द बुद्ध, स्ट्रॉन्ग [https://books.google.co.in/books?id=xqAB6PKyP98C&pg=PA136&redir_esc=y#v=onepage&q&f=false जॉन]</ref> अशोक के नाँव उनके धइल रहे। दिव्यदानो मे अइसने कहानी लिखल बा लेकिन माई के नाँव जनकपदकल्यानी लिखल बा।
'''सत्ता के बिस्तार'''
272इशा पूर्व में राजा बिन्दुसार के मरला के बाद राजगद्दी खातिर लड़ाई चालू हो गइल।दिव्यदान में लिखल बा कि, बिन्दुसार आपन बड़ बेटा [[सूसिम]] के आपन उत्तराधिकारी बनावल चाहत रहन, बाकिर अशोक के आपन पिता के मंत्री लोगन के सहजोग मिलल रहे। मंत्री लोग सूसिम के अहंकारी मानत रहन, आउर ई लोग के लागत रहे की सूसिम उ लोग के इज्जत ना करी।<ref>[a][https://books.google.co.in/books?id=jbjT9GG__nEC&pg=PA268&redir_esc=y#v=onepage&q&f=false]</ref> पता लागेला कि एगो रधागुप्त नांव के मंत्री अशोक के राजगद्दी ले पहुचावे में महत्वपूर्ण भूमिका निभवले रहे। अशोकवदन मे एह बात के लीखल गइल बा कि राजा बनला के बाद अशोक रधागुप्त के प्रधानमंत्री बनवले रहन। [[दिपवंश]] आ [[महावंश]] मे लिखल बा कि अशोक आपन 99 भाईयन के मार के राजा बनल रहन, बाकिर एह बात के सबूत ना मिले।
राजा बनला के बाद अशोक आपन राज्य के बहुत तेजी से बिस्तार कीइलें। हुनकर राज उत्तर-पच्छिम में हिंदुकुश से ले के पूरुब में वर्तमान बांग्लादेश ले, आ दक्खिन के ओरे आजकाल के तमिलनाडु आ केरल के कुछ हिस्सा छोड़ के बाकी मय भारत प बिस्तार वाला रहल, उनकर राज बलुचिस्तान आ अफगानिस्तानो पे रहल।
'''बियाह'''
[[चित्र:Emperor Ashoka and his Queen at the Deer Park.jpg|thumb|middle|अशोक आपन रानी लोग साथे]]
<small>अलग-अलग स्त्रोतन से पता लागेला कि अशोक के पाँच गो बियाह भइल रहे। उ लोग के नाँव देवी, करुवाकी, तिष्यारक्षा पद्मावती आउर असन्धिमित्रा रहे। अइसहीँ ईहो पता लागेला कि अशोक चार बेटा आ दुगो बेटी के बाप रहन। बेटन के नाम महेन्द्र(देवी के बेटा), तिवारा (करुवाकी के बेटा), कुनाल (पद्मावती के बेटा), आउर जकुला रहे। बेटी लोग के नाम संघमित्रा(देवी के बेटी) आ चारूमती रहे।</small><ref>Ashoka by मुखर्जी [https://books.google.co.in/books?id=uXyftdtE1ygC रधकुमुंद]</ref>
'''कलिंगा पऽ बिजय आ बुद्ध धर्म में परिवर्तन'''
भले अशोक के जीवन के जीवन खून खराबा से भरल रहे बाकिर कलिंगा पऽ बिजय हासिल कइला के बाद उ बुद्ध धर्म के अपना लिहलें।[[कलिंग]] राज्य ओह समय में एगो गणतांत्रिक राज्य रहे जवन की ओह घड़ी के भारत में एगो अपवाद नियन रहे, काहे की ओह घड़ी राजतंत्र रहे। अशोक के गद्दी पऽ बइठला के आठ साल बाद [[कलिंग युद्ध]] भइल। शिलालेखन से पता लागेला की युद्ध बड़ी भयानक रहे जेमे लगभग एक लाख सैनिक मारल गइलें।
कहल जाला की कलिंग पऽ बिजय हासिल कइला के बाद , एक दिन अशोक नगर घूमे गइलें जेने उनका खाली तबाही लउकल। युद्ध से भइल ओह तबाही उनका के बदल दिहलस, आ ओकर बाद उ बुद्ध धर्म के अपना लिहलन।
'''देहांत आउर बिरासत'''
अशोक 36 साल लें राज कइलन, हुनकर 232 ई•पु में देहांत हो गइल। कहल जाला की हुनकर अंतिम संस्कार बजी उनकर लाश सात दिन आउर सात रातन ले जलत रहल।उनका मरला के बाद [[मौर्य साम्राज्य]] 50 साल में ही खतम हो गइल।
185 ई•पु मे अंतिम मौर्य सम्राट बृहदरथ के हत्या हुनकर सेनापति द्वारा कर दिहल गइल। जेकरा बाद [[पुष्पमित्र सूंघ]] राजा बनलें आ सुंघ वंश के स्थापना कीइलें।
अशोक के बारे जादे तर बात शीलालेखन से पता लागेला जवन की [[प्राकृत भाषा]] में लिखल बा।
== एतेहासिक स्त्रोत ==
अंग्रेजन के समय के इतिहासकार लगभग अशोक के भुला गइल रहन, बाकिर [[जेम्स प्रिंसपी]] ऐतिहासिक स्त्रोतन के फेनू से जोहे के काम कइलन। [[जॉन ह्यूबर्ट मार्शल]] एगो आउर महत्वपूर्ण इतिहासकार रहन।
[[चित्र:AsokaKandahar.jpg|thumb|middle|अशोक के एगो शिलालेख]]
अशोक के जीवन आ शासन के जानकारी जादे तर बुद्ध स्त्रोतन से मिलेला। अशोकवदन से अशोक के बारे में ढेर जानकारी मिल जाला,आउर जानकारी दीपवंश,महावंश आ [[अशोक के शिलालेख]] सभ से मीलेला। अशोक के घड़ी के चीज [[कुम्हार]],पटना मे मिलेला।
अशोक के अध्यादेश:- अशोक के अध्यादेश 33 गो शिलालेखन के समूह बा। ई शिलालेख सभ [[भारत]] आ [[पाकिस्तान]] में फैलल बा। ई शिलालेख सभ भारत के इतिहास के एगो सभसे बरियार राजा द्वारा बुद्ध धरम के परचार के कहानी बतवेला।
[[महावंश]]:- ई [[श्रीलंका]] के राजा लोग के लिखल एगो कविता हऽ जेकरा [[पाली]] भाषा में लिखल गइल बा।
== धारना सभ आउर इतिहास लेखन ==
अशोक के जीवन के जानकारी फेनु से प्राप्त करे में सभ से जादे बुद्ध स्त्रोत के इस्तेमाल कइल गइल बा। पारंपरिक लेखन के आधार पऽ शुरुआती विद्वान अशोक के एगो बौद्ध सम्राट मनले बाड़न, जेकरा धर्म बदले के पड़ल रहे,आ जे बुद्ध धर्म के परचार कइले रहे। रोमिला थापर अशोक के बारे में लिखले बाड़ी की " हमनी के अशोक के दू तरह से देखे के चाहीं, पाहिला एगो राजनेता जवन एक समय में आपन साम्राज्य स्थापित कइलन, आ दूसरा एगो अइसन आदमी जे समाज के बदले खातिर आपन आचार विचार के समाज में फैलीईलें। अशोक अपना घड़ी के हर धर्म के इज्जत करत रहन।
हालाकि शिलालेख से ई बात साफ पता लागेला की अशोक बुद्ध धर्म के मानत रहन। एगो शिलालेख में अशोक जानवर सभ के बली देवे वाला वैदिक अनुष्ठान के प्रतिबंधित कइले बाड़न, एकरा से ई बात साबित होखेला की ऊ वैदिक परंपरा के ना मानत रहन। ऊ बुद्ध धर्म के पवित्र जगहन पऽ आपन स्तंभ खड़ा करवईलें बाकिर बाकी सभ धर्म के पवित्र जगह पऽ स्तंभ ना बनववलन। अशोक कर्तव्यन के देखावे ला "धम्म" शब्द के इस्तेमाल कइलन,जवन एगो बौद्ध शब्द हऽ। साथे सारे ऊ बुद्ध धर्म के तीन गो उपदेशनो के परचार कइलन।
अशोक के सभ शिलालेखन में अशोक के एगो निमन आ दयालु राजा देखावल गइल बा। कलिंग के शिलालेखन में अशोक अपना के आपन प्रजा के पिता कहले बाड़न आउर आपन प्रजा के आपन संतान बोलले बाड़ें।<ref>http://www.cs.colostate.edu/~malaiya/ashoka.html</ref> शिलालेखन में अशोक बुद्ध धरम के शिक्षण के लिखववले बाड़न आ अहींसा के अपनावे के कहले बाड़न, साथे साथे ऊ आपन राज के बारे मे बतवले बाड़ें । [[सारनाथ]] के अशोक स्तंभ अशोक के समय के सभ से प्रसिद्ध चीज हऽ। बलुआ पत्थर के बनल स्तंभ अशोक के सारनाथ यात्रा के बारे में बतावेला। स्तंभ के ऊपर चारो दिशा में चार गो शेर के मुह बनल बा जे अशोक के बड़हन आ बरियार राज्य के दर्शावेला। शिलालेखन से मिलल जादे तर जानकारी के इतिहासकार सही मानेलन, लेकिन ई बतावल मुश्किल बा की कुछ चीज साँचो भइल रहे की ना।
अशोक के शिलालेख सभ के खोज से पहिले अशोक के बारे में ढ़ेर कहानी प्रचलित रहे जवन की ऐतिहासिक स्त्रोत पऽ आधारित ना रहे। ओह में से कुछ कहानी अशोकवदन में से बा:-
1) ओह में से एगो कहानी जय नाम के एगो लईका के हऽ, एक दिन जब जय सड़क के किनारे खेलत रहे, जहा बुद्ध आ गइलन।छोटहन जय बुद्ध केभीख के कटोरा में एक मुट्ठी माटी रख के आगे चल के बहुत बड़ राजा आ बुद्ध धर्म के अनुयाई बन के वरदान मंगलस।आगे लिखल बा की बुद्ध पूरा ब्रह्माण्ड के अंजोर कऽ दिहलन आ फिर ऊ अंजोरा के आपन बाया हाथ में लेके बोललन की ई लाईका आपन अगीला जनम में बहुत बड़ राजा बनी। बुद्ध ईहो भविष्यवाणी कइलें की हेकर राजधानी पाटलिपुत्र होई।
2)अगिला कहनी में अशोक के एगो खराब आदमी देखावल गइल बा, जे बुद्ध धरम अपनवला के बाद निक आदीमी बनऽता। लिखल बा कि अशोक के शारीरिक कुरूपता के चलते [[बिन्दुसा|बिन्दुसार]] ओकरा के पसंद ना करत रहन। अशोक राजा बनल चाहत रहन एही चलते उ वारिस के जरत कोयला से भरल गड्ढा में डाल दिहलन। उ आपन खराब स्वभाव आउर खिस के चलते "भयंकर अशोक" नाम से प्रसिद्ध भइलन। कहल जाला की उ आपन सैनीकन के वफादारी के परिक्षा खातीर ले गइल रहन, आ सफल ना होला पऽ 500 आदमी के मार दिहलन कहल जाला कि जब कुछ औरत लोग उनकर अपमान कइलस तऽ उ आपन हरम के जरा के मार दिहलन। मानल जाला कि उ लोगन के उदास देखिके आनंद उठावत रहन आउर एकरा खातिर उ एगो बड़हन आउर भयावह उत्पीड़न कक्ष बनईलन जेने उ आउर लोग के यातना देवत रहन। कहानी आगे ई बतावे ले ह कि कईसे एगो पवित्र बौद्ध भिक्षु से मिलला के बाद अशोक धार्मिक बन जा ताड़न।
3) एगो आउर कहानी मे कहल गइल बा की आपन खजाना के बौद्ध संघ उपहार देवल चालु कर देलन। मंत्री सभ के डर लागत रहे की एकरा से साम्राज्य के पतन हो जाई, ईहे चलते अशोक के मना करल गइल।फिर अशोक आपन निजी धन के उपहार मे देवे लगलन। अंत मे कुछ ना बाचल आ हुनकर शांति से निधन भइल।
एह बिंदु पऽ इ ध्यान रखल ज़रूरी बा कि अशोकवदन एगो बौद्ध पाठ हऽ, जे बौद्ध धर्म मे नाया लोग जोड़े ला लिखल रहे आउर एही चलते इ सभ किंवदंतियन के इस्तेमाल कइल गइल रहे। एह तरह के ग्रंथन मे कहल गइल बा कि अशोक मूलतः आदर्श बौद्ध शासक रहन, जे प्रशंसा आउर अनुकरण दुनो के हकदार रहन।
'''अशोक आउर बुद्ध के अवशेष'''
बौद्ध कथा , विशेष रूप से [[महापरिनिर्वाण]], के अनुसार बुद्ध के अवशेष हुनकर मरला के बाद आठ देशों में साझा कइल गइल। अशोक अवशेष वापस लेवे के काम कइलन आउर 84000 स्तूपन में बांट देलन। ई कहानी सांची आउर भरहुत में स्पष्ट रूप से देखाईल गइल बा।
== योगदान ==
[[चित्र:Sanchi2.jpg|thumb|middle|सांची के स्तूप]]
एगो बौद्ध राजा होला के नाते,अशोक मानत रहन की बुद्ध धरम आदमी,जानवर, पौधा सभ जीव जंतु खातिर निक बा, एही चलते उ ढेरे स्तूप आ बौद्ध भिख्छु के रहे खातिर घर बनववलें। अशोकवदन में लिखल बा कि अशोक बुद्ध के अवशेष के रखे खातिर 84000 स्तूप बनाए के आदेश देले रहन। अशोक आपन बेटा [[महेंद्र]] आउर बेटी [[संघमित्रा]] के बौद्ध धर्म के परचार खातिर श्रीलंका भेजले रहन।
''' व्यवस्थापक के रूप में '''
[[चित्र:MauryanRingstone.JPG|thumb|middle|मौर्य काल के एगो पत्थर]]
अशोक के सेना बरियार रहे, बाकिर बौद्ध धर्म के अपनवला के बाद, ऊ दक्षिण में तीन गो प्रमुख तमिल राज्य चेरस, चोल आ पंड्या के जोरे संघतिया नियन संबंध बनईलन। हुनकर शिलालेखन में कहल गइल बा कि ऊ आपने राज्य आ पड़ोसी राज्यन में लोग आउर जानवरन के चिकित्सा खातीर प्रावधान कइले बाड़न। उ आम लोगन के लाभ खातीर सड़कन पऽ ईनार खोदववले आ पेड़ लगववले बाड़े।
''' जानवर सभ के कल्यान '''
अशोक के शीलालेखन में ई बात साफ लिखल गइल बा की जानवर सभ के दुख देवल केनियो ले नीक काम नइखे।<ref>https://en.m.wikipedia.org/wiki/Ashoka#CITEREFFitzgerald2004</ref> हालाकि ऊ गाय के मांस खाए पऽ रोक ना लगवले रहन।<ref>https://books.google.co.in/books?id=JwGZTQunH00C&pg=PA108&redir_esc=y#v=onepage&q&f=false</ref>
ऊ उ सभ चार गोड़ वाला जानवरन के मारे पऽ रोक लगा देले रहन जवना के ना कवनो इस्तेमाल रहे आउर ना खाईल जा सकत रहे। संगे-संगे कुछ बिशेष परजाति के चिड़ई आ जानवर के मारे पे रोक लगा देले रहन। एतने ना बलुक उ सभ मादा बकरी, सुअर आ भेड़ के मारे पऽ रोक रहे जवन आपन बच्चा के देखभाल करत होखे।
अशोक जानवर के मरला पऽ रोक लगवले रहन आ ओखिनी के इलाज ला अस्पतालो बनववले रहन। अशोक के घड़ी के मौर्य साम्राज्य दुनिया के गीनल चुनल साम्राज्य में आवेला जे आपन जानवर के नागरिक नियन राखत रहे।<ref>https://en.m.wikipedia.org/wiki/Special:BookSources/1590561066</ref>
''' अशोक चक्र '''
[[चित्र:Ashoka Chakra.svg|thumb|middle|[[अशोक चक्र]]]]
[[अशोक चक्र]] धर्मचक्र के एगो चित्रण बा। एह में चौबीस गो तिली होखला। अशोक चक्र के जगह-जगह बनवावल गइल रहे। आज-काल अशोक चक्र सभ से जादे भारत के झंडा पऽ लउकेला। अशोक चक्र के भारत के झण्डा के उजरका पट्टी पऽ आसमानी रङ्ग से बनावल गइल बा। अशोक चक्र के [[अशोक स्तंभ]] पे भी देखावल गइल बा।
'''वास्तुकला '''
अक्सर अशोक के भारत में पत्थर पऽ वास्तुकला चालू करवाए के श्रेय दिहल जाला, जवन की संभवतः यूनानी सभ से सीखल गइल रहे।<ref>[https://books.google.co.in/books?id=fpfTAgAAQBAJ&pg=PT21&redir_esc=y#v=onepage&q&f=false a]</ref> अशोक से पहिले घर आ मकान अस्थाई समान से बनत रहे जईसे लकड़ी, [[बांस]] आ खपड़ा।<ref>[https://books.google.co.in/books?id=TlVeuxIgjwQC&pg=PA14&redir_esc=y#v=onepage&q&f=false सी]</ref> [[पाटलिपुत्र]] में अशोक आपन लकड़ी के महल के पत्थर के बंववलें।<ref>Ashoka was known to be a great builder who may have even imported craftsmen from abroad to build royal monuments." Monuments, Power and Poverty in India: From Ashoka to the Raj, A. S. Bhalla, I.B.Tauris, 2015 p.18</ref> ओह महल के बनवावे में उ बिदेसी कारीगर के इस्तेमाल कइले रहन।
''' अशोक स्तंभ '''
[[चित्र:Ashok Sthamba.jpg|thumb|middle|[[लुंबिनी]],[[नेपाल]](जहवाँ बुद्ध जन्मल रहन) में बनवावल स्तम्भ]]
अशोक जगह जगह स्तंभ बनववले रहन। अईसे तऽ अशोक के ढ़ेरे स्तंभ होखे के चाहीँ लेकिन अभी खाली दसे गो बा। स्तंभ सभ के औसत ऊंचाई 40-50 फीट आ वजन 50,000 किलोग्राम बा। सँवसे स्तंभ सभ के बनारस में तरासल गइल रहे आ फिर जगह जगह ले जा के खड़ा कइल गइल रहे।स्तंभ सभ के खोज सोरहवाँ शताब्दी में थॉमस कॉर्यात कइलें रहन।
''' सिंह चतुर्भुज स्तंभ शीर्ष '''
[[चित्र:Sarnath capital.jpg|thumb|middle|भारत के राष्ट्रीय चिह्न]]
सिंह चतुर्भुज स्तंभ शीर्ष अशोक के [[सारनाथ]] के स्तंभ पऽ धईल रहे, बाकिर अब उ [[सारनाथ]] संग्रहालय में धईल बा।एकरा भारत के राष्ट्रीय चिन्ह के रूप में भी अपनावल गइल बा।
== कला, चलचित्र आ साहित्य में ==
[[चित्र:Asoka's Queen.jpg|thumb|middle|[[अबनिन्द्रनाथ टैगोर]] के बनावल चित्र जेमे अशोक के रानी के सांची के स्तूप भिरी खड़ा देखावल गइल बा।]]
• [[जयशंकर प्रसाद]] 'अशोक की चिंता' (अशोक के चिंता) नाम के एगो कविता लीखले बाड़े, जेह में [[कलिंग]] युद्ध घड़ी अशोक के विचार लिखल गइल बा।
• 1941 में [[अशोक कुमार]] नाम के तमिल फिल्म बनवाल गइल रहे जवन अशोक के बारे में रहे।
• 1973 में [[अमर चित्रकथा]] अशोक के जीवन पऽ एगो किताब प्रकाशित कइले रहे।
• 2001 में अशोक नाम के एगो फिल्म बनल रहे जेह में [[शाहरुख खान]] काम कइले रहन।
== इहो देखल जाय ==
* [[अशोक के आदेसलेख]]
== संदर्भ ==
{{Reflist|35em}}
== स्रोत सामग्री ==
* {{citation |last=Singh |first=Upinder |authorlink=Upinder Singh |title=A history of ancient and early medieval India : from the Stone Age to the 12th century |date=2008 |publisher=[[Pearson Education]] |location=[[New Delhi]] |isbn=978-81-317-1120-0|url=https://books.google.co.in/books?id=GW5Gx0HSXKUC |ref={{sfnref|Upinder Singh|2008}} }}
== बाहरी कड़ी ==
{{Commons category|Ashoka|अशोक}}
{{wikiquote}}
* BBC Radio 4: Sunil Khilnani, ''[http://www.bbc.co.uk/programmes/b05tpy82 Incarnations: Ashoka.]''
* BBC Radio 4: Melvyn Bragg with Richard Gombrich et al., ''[http://www.bbc.co.uk/programmes/b0511tm1 In Our Time, Ashoka the Great]''.
* {{Cite book|title = Inscriptions of Asoka: New Edition|last = Hultzsch|first = E.|publisher = Government of India|year = 1925|isbn = |location = Oxford|pages = |url = https://archive.org/details/InscriptionsOfAsoka.NewEditionByE.Hultzsch}}
{{S-start}}
{{S-hou|[[मौर्य वंश]]||304 BCE||232 BCE}}
{{S-bef|before=[[बिंदुसार]]}}
{{S-ttl|title=[[मौर्य साम्राज्य|मौर्य सम्राट]]
|years=272–232 BCE}}
{{S-aft|after=[[दशरथ मौर्य|दशरथ]]}}
{{s-end}}
{{Buddhism topics}}
{{Authority control}}
[[श्रेणी:3री-सदी ईसापूर्ब के भारतीय राजा]]
[[श्रेणी:मौर्य सम्राट]]
[[श्रेणी:अशोक| ]]
ou7z71dtv9akehpfyb4s47cg5frweui
मथुरा
0
37477
803297
785212
2026-07-29T16:06:40Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803297
wikitext
text/x-wiki
{{Infobox settlement
| name = मथुरा
| native_name =
| native_name_lang = <!-- ISO 639-2 code e.g. "fr" for French. If more than one, use {{lang}} instead -->
| settlement_type = [[शहर]]
| image_skyline =
| image_alt =
| image_caption =
| etymology =
| nickname =
| population_as_of = 2011
| population_footnotes =
| population_total = 454937
| latd =
| longd =
| coordinates_type =
| coordinates_region =
| coordinates_display = inline,title
| subdivision_type = देस
| subdivision_name = {{flag|भारत}}
| subdivision_type1 = [[भारत के राज्य अउरी संघ राज्यक्षेत्र|राज्य]]
| subdivision_name1 = [[उत्तर प्रदेश]]
| subdivision_type2 =
| subdivision_name2 =
| website = <!-- {{URL|example.com}} -->
}}
'''मथुरा''' ({{Lang|en|[[:en:Mathura|Mathura]]}}) भारत के [[उत्तर प्रदेश]] राज्य में एक ठो शहर बा।मथुरा एगो इतिहासी एवं धार्मिक पर्यटन स्थल की रूप में प्रसिद्ध बा। लंबा समय से मथुरा प्राचीन भारतीय संस्कृति एवं सभ्यता क केंद्र रहल बा। भारतीय धर्म, दर्शन कला एवं साहित्य की निर्माण तथा विकास में मथुरा का महत्वपूर्ण योगदान सदा से रहल बा। आजो महाकवि सूरदास, संगीत क आचार्य स्वामी हरिदास, स्वामी दयानंद क गुरु स्वामी विरजानंद, कवि रसखान आदि महान हस्तीयन से ए नगरी क नाम जुड़ल बा। मथुरा के श्रीकृष्ण जन्म भूमि की नांव से भी जानल जाला।<ref name=Prasad>[https://books.google.com/books?id=o0_5caqiUH0C&pg=PT22&dq=Krishna+Janmasthan&hl=en&sa=X&ved=0ahUKEwj1-6mYj6XOAhUBEz4KHTL5CTcQ6AEIHjAA#v=onepage&q=Krishna%20Janmasthan&f=false Krishna: A Journey through the Lands & Legends of Krishna], by Dev Prasad</ref>
[[File:*Bindrabun -Vrindavan-. Gate of Shet Lukhmeechund's Temple; a photo by Eugene Clutterbuck Impey, 1860's.jpg|thumb|upright|Gate of Shet Lukhmeechund's Temple, a photo by Eugene Clutterbuck Impey, 1860's.]]
==मथुरा क परिचय==
[[File:Great Emperor Kanishka - Greatest of Kushan Monarchs - Circa 1st Century CE - Mathura - Government Museum - Mathura 2013-02-23 5836.JPG|thumb|upright|Sculpture from 1st century CE on display at Mathura museum.]]
मथुरा, भगवान कृष्ण क जन्मस्थली और भारत क परम प्राचीन तथा जगद्-विख्यात नगरी ह। शूरसेन देश क एइजा राजधानी रहे।<ref name=ImpGazetteer>{{cite web|title=Imperial Gazetteer of India. v. 18.|url=http://dsal.uchicago.edu/reference/gazetteer/toc.html?volume=18|work=Digital South Asia Library|pages=63–74|year=1908|access-date=2017-06-16|archive-date=2017-08-26|archive-url=https://web.archive.org/web/20170826213103/http://dsal.uchicago.edu/reference/gazetteer/toc.html?volume=18|url-status=dead}}</ref> पौराणिक साहित्य में मथुरा के अनेक नांव से संबोधित कइल गइल बा जैसे- शूरसेन नगरी, मधुपुरी, मधुनगरी, मधुरा आदि। भारतवर्ष क उ भाग जवन हिमालय और विंध्याचल की बीच में पड़ेला ओके प्राचीनकाल में आर्यावर्त कहल जाव। एइजा पनपल भारतीय संस्कृति के जवन धारा सींचली स उ गंगा और यमुना क धारा रहली स। ए दुनुं नदी की किनारे भारतीय संस्कृति क कईगो केन्द्र बनन स और विकसित भई न स।
वाराणसी, प्रयाग, कौशाम्बी, हस्तिनापुर,कन्नौज आदि केतना ऐसन स्थान हंव स, परन्तु इ तालिका तबले पूर्ण नाहीं हो सकेले जबले एमें मथुरा क समावेश न कइल जाव। इ आगरा से दिल्ली की ओर और दिल्ली से आगरा की ओर क्रमश: 58 किलोमीटर उत्तर-पश्चिम एवं 145 किलोमीटर दक्षिण-पश्चिम में यमुना की किनारे राष्ट्रीय राजमार्ग 2 पर स्थित बा।
वाल्मीकि रामायण में मथुरा के मधुपुर चाहें मधुदानव क नगर कहल गइल बा तथा एइजा लवणासुर क राजधानी बतावल गइल बा- ए नगरी के ए प्रसंग में मधुदैत्य द्वारा बसावल, बतावल गइल बा। लवणासुर, जेके शत्रुघ्न युद्ध में हरा के मारले रह न उ एही मधुदानव क पुत्र रह न।
==जनसांख्यिकी==
साल 2011 के जनगणना<ref name="census2011">{{cite web|title=City Census 2011|url=http://www.census2011.co.in/city.php|website=census2011.co.in|accessdate=22 नवंबर 2016}}</ref> अनुसार मथुरा के कुल शहरी जनसंख्या 349909 बाटे आ आसपास के उपशहरी इलाका के मिला लिहल जाय तब कुल जनसंख्या 454937 बा। जनसंख्या के हिसाब से ई भारत के 125वाँ शहर बाटे।
जनगणना आँकड़ा के मोताबिक एह शहर में [[लिंगानुपात]] 881 आ [[साक्षरता दर|साक्षरता के दर]] 74.97% बाटे।<ref name="census2011" />
{{clear}}
==संदर्भ==
{{reflist}}
==बाहरी कड़ी==
{{Commons category|Mathura}}
* [http://www.mathuracity.in Mathura City E-Portal] {{Webarchive|url=https://web.archive.org/web/20200229100541/http://www.mathuracity.in/ |date=2020-02-29 }}
* [http://www.palikanon.com/english/pali_names/ma/madhuraa.htm Entry on Mathura in the Dictionary on Pali Proper Names]
* [http://hridayindia.in/mathura/ Mathura HRIDAY city]
[[श्रेणी:भारत के शहर आ कस्बा]]
[[श्रेणी:उत्तर प्रदेश के शहर]]
[[श्रेणी:पबित्र हिंदू नगर]]
{{India-geo-stub}}
klo5pjjbg22qwb801b7t04h5a6zyk29
शेर शाह सूरी
0
38905
803336
765865
2026-07-29T16:21:42Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803336
wikitext
text/x-wiki
{{Infobox royalty
| title = पादिशाह
| image =
| caption = शेर शाह के कल्पानिक छावि, अब्दुल गफूर के बनावल
| succession = [[सूरी|सुर कुल कर सुलतान]]
| reign = 17 मई 1538 – 22 मई1545
| coronation = 1540
| predecessor = [[हुमायूँ]]
| successor = इस्लाम शाह सूरी
| spouse = उत्मादुन निस्सा बानो बेगम
रानी शाह बेगम
| issue = इस्लाम शाह सूरी (जलाल खान)<br /> अदिल खान
| full name = फरिद खान लोधी
| house =
| dynasty = सुर साम्राज्य
| father = हसन खान सूरी
| birth_date = 1486
| birth_place = [[सासाराम]], [[दिल्ली सल्तनत]] (अखनी [[बिहार]] मे, [[भारत]]<ref name="Sarwani"/>{{npsn|date=December 2016}}
| death_date = 22 मई 1545 (उमिर 58–59)
| death_place = कालिंजर
| place of burial = [[शेर शाह सुरी के मकबरा]], सासाराम
| religion = [[इस्लाम]]
|}}
'''शाह सूरी'''(𑂬𑂵𑂩 𑂬𑂰𑂯 𑂮𑂴𑂩𑂲) (1486 - 22 मई 1545) [[उत्तर भारत]] में सूरी साम्राज्य के संस्थापक रहलें। एह साम्राज्य के राजधानी [[सासाराम]] रहल। शेर शाह एक मूल रूप से पख्तून अफगानी रहलें आ 1540 में मुगलन से सत्ता हासिल कइलेन। एक ठो दुर्घटना में उनके मौत के बाद उनके लड़िका इस्लाम शाह सत्ता पवलें। राजा बने से पहिले ई मुगल सेना मे एगो सपिही रहन, फेर ई [[बाबर]] के पूत [[हुमायूँ]] के हरा के अपने राजा बन जा तताड़ऽन।
आपन सात बछर के राज मे ई बहुते नीमन नीमन काज कइलन आ परिवर्तन कइलन। हीनकर कुछु कुछु काज के मुगल राजा लोग, जइसे [[अकबर]] आगे बढ़वलस। ई हुमायूँ के ''दिना पनाह'' नगर के बिकास कइलन आ होकर नांव शेरगढ़ धइलन आ [[पाटलिपुत्र]] जेेकर सतमा सदी मे ओरा गइल रहे होके फेर से थापित कइलन।
==जिनगी==
शेर शाह सूरी एगो पशतुन कुल मे [[सासाराम]] जनमल रहन, जेवन की अबही [[बिहार]] मे बाटे।<ref>Asad Muḥammad K̲h̲ān̲, ''The Harvest of Anger and Other Stories'', Oxford University Press (2002), p. 62</ref><ref>[[Ishwari Prasad]], ''The Mughal Empire'', Chugh Publications (1974), p. 157</ref> हिनकर उपनाँव पश्तुन के सुर जात से लिहल बाटे। कहल जाला जे, हीनकर नांव ''शेर'' एह से पड़ल काहे कि ई जहिया सेयान रहन तहिया एक हाली एगो बाघ से लड़ल रहन आ होके मुवा दिहले रहन।<ref name=":0">{{cite book |last1=Lane-Poole |first1=Stanley |author-link=Stanley Lane-Poole |year=2007 |orig-year=First published 1903 |title=Medieval India: under Mohammedan rule (A.D. 712-1764) |publisher=Sang-e-Meel Publications |page=236 |isbn=978-969-35-2052-1}}</ref><ref name=EBSur>{{cite web|title=Sur Dynasty|url=http://www.britannica.com/topic/Sur-dynasty|website=Encyclopædia Britannica|access-date=5 December 2015}}</ref> हिनकर आजा इब्राहिम खान सुरी एगो जागीरदार रहन। ई मियां हसन खान सुरी के आठ गो पुत मे से एगो रहन।
लईकाई मे फरीद आपन घरे से भाग गइलन, जब हुनकर पिता, हसन खान के ई बात पता लगाल जे उ [[जौनपुर]] के राजा जमाल खान लगे गइल बाड़न तऽ ऊ जमाल के एगो पाती लीखलन आ कहलन:
{{quote|फरीद खान, हमरा से खिसिया के, बिना केवनो कारन के रउरा लगे चलि गइल बाड़ें। हमरा रउवा प पूरा बिस्वास बा जे रउवा हुनका के मना लिहब, आ बिदा कऽ दिहब; बाकिर जदि उ राऊर बाति नइखन सुनत, आ नइखन आवत, तऽ हमरा बिस्वास बा जे रउवा हुनकरा अपना भीरी धऽ लिहब, हम आसा करब जे ओहिजा हुनका धरम के सिख मिली<ref name="Sarwari-79">{{cite web |url=http://persian.packhum.org/persian/pf?file=80201014&ct=79|title=Táríkh-i Sher Sháhí; or, Tuhfat-i Akbar Sháhí, of 'Abbás Khán Sarwání. CHAPTER I. Account of the reign of Sher Sháh Súr.|page=79|author=Abbas Khan Sarwani|work=Sir H. M. Elliot|location=London|publisher=[[Packard Humanities Institute]]|year=1580|access-date=4 September 2010|author-link=Tarikh-i-Sher Shahi}}</ref>}}
जमाल खान, फरीद से घरे जाए के कहलन बाकिर फरीद ई बात ना सकरलें, एगो दोसर पाती मे उ लिखलन:
{{quote|जदि हमार बाबूजी चाहऽतानी जे हम सिक्खा लिहीं, तऽ एह नगर मे ढेर पढ़ल लिखल जन बा: हम एहिजा पढ़ब<ref name="Sarwari-79"/>}}
==बिहार आ बंगाल के बीजऽय==
{{Quote box|quote= शेर शाह मलक (फरिश्ता) ना रहन बलुक मालिक (राजा) रहन। छव बछर मे, ऊ राज के स्थाई कइलन जेकर नेंव आजुओ बाचल बा। हुनकर भारत मे कइल काज इरान आ तुरान के राजा लोग मन के भा गइल आ ऊ लोग एकरा देखल चाहेला। हज़रत अर्श ऐशियानी (अकबर) हुनकरे कइल काज (ज़वाबित) के अगिला पचास बछल रहे देलन आ ओके कोनो बदलाव ना कइलन। आजु उहे भारत मे बन छोड़ि के कुछुवो नइखे बाचल।|source=मिर्जा आजिज़ कोका, अताग खान के पूत, के लिखल [[जहाँगीर]] लगे एगो पाती|align=centre|width=100%|fontsize=85%|quoted=1}}
शेर शाह बिहार के मुगल प्रशासक, बहर खान लोहनी, ला काम कइल चालू कइलन।<ref name=Britannica/><ref name=bpedia>{{cite book |last=अली |first=मुहम्मद अंसार |year=2012 |chapter=शेर शाह |chapter-url=http://en.banglapedia.org/index.php?title=Sher_Shah |editor1-last=Islam |editor1-first=Sirajul |editor1-link=Sirajul Islam |editor2-last=Jamal |editor2-first=Ahmed A. |title=Banglapedia: National Encyclopedia of Bangladesh |edition=Second |publisher=[[Asiatic Society of Bangladesh]]}}</ref> हुनकर बीरता के चलते बहर खान हुनकरा शेर खान कहत रहन। बहर खान के मुवला के बाद, शेर खान, जमाल खान के प्रतिनिधी राजा बनलन। बादि मे शेर खान के बढ़त तागत के देखि के जमाल खान बंगाल के सुल्तान गियासुद्दीन मुहम्मद शाह के मदद मंगलन। गियासुद्दीन शाह, इब्राहिम खान के नेतृत्व मे आपन सेना भेजलन, बकीर शेर खान उज्जैनीया राजपूतन संघे मिली के, 1534 मे, सूरजगढ़ के जुध मे ओह सेना ले जीत गइलन।<ref>{{cite book |last=Ahmad |first=Imtiaz |year=2008 |chapter=State Formation and Consolidation under the Ujjaniya Rajputs |editor1=Surinder Singh |editor2=Ishawr Dayal Gaur |title=Popular Literature and Pre-modern Societies in South Asia |chapter-url=https://books.google.com/books?id=QVA0JAzQJkYC&pg=PA80 |publisher=Pearson Education India |page=80 |isbn=978-81-317-1358-7}}</ref> एकरा बादि बिहार प हुनकर अधिकार हो जाता।
1538 मे शेर खान, बंगाल प आक्रमन कइलन आ मुहम्मद शाह के हरा देलन, बाकिर हुमायूँ के चलते ओह राज प ऊ कब्जा ना कई पइलन। 26 जुन 1539 के, शेर खान आउर [[हुमायूँ]] मे [[चउसा के जुध]] भइल जेहमे शेर खान जई भइलन। मई 1540 मे शेर खान, हुमायूँ के फेर कन्नौज मे हरइलन आ हुमायूँ के भारत छोरे पड़ल।<ref name=Britannica /><ref name="Wolseley Haig">{{cite book |last1=Haig |first1=Wolseley |author-link1=Wolseley Haig |year=1962 |orig-year=First published 1937 |chapter=Sher Shah and the Sur Dynasty |chapter-url=https://books.google.com/books?id=yoI8AAAAIAAJ&pg=PA51 |editor1-last=Burn |editor1-first=Richard |editor1-link=Richard Burn (Indologist) |title=The Cambridge History of India |publisher=Cambridge University Press |volume=Volume IV: The Mughal Period |page=51 |access-date=16 November 2016 |language=en}}</ref>
==मालवा के बीजऽय==
1537 मे गुजरात के बहादुर शाह मे मूवला के बादि, मालवा कर नवका राजा कादिर शाह बनलन।
==मारवाड़ के बीजऽय==
1543 मे शेर शाह सूरी एगो 80,000 लड़ाका वाला बिसाल सेना लेके मरवाड़ के राजा मालदेव राठौर से जुध कइलन आ हुनकरा हरा देलन आ जोधपुर प अधिकार हासिल कई लिहलन।
==राज काज==
[[File:An inspection of Sher Shah Sur's Great North Road.jpg|thumb|एगो राह के जायजा लेत शेर शाह सूरी]]
[[File:Sher shah's rupee.jpg|thumb|right|शेर शाह सूरी के रूपिया]]
शेर शाह सूरी तीन परकार के सिकका चलवलें। शेर शाह से पहिले रूपिया चानी के सिक्का के कहल जात रहे बाकिर शेर शाह सूरी, एक रूपिया मे लागे वाला चानी के भार के निश्चित (178 ग्राम) कई दिहलन, एही रूपिया आजो भारत के मुद्रा बाटे।<ref name="RBI">{{cite web |url=http://www.rbi.org.in/currency/museum/c-mogul.html |title=Mughal Coinage |website=[[RBI Monetary Museum]] |publisher=Reserve Bank of India |access-date=24 August 2010 |url-status=dead |archive-url=https://web.archive.org/web/20080516085855/http://www.rbi.org.in/currency/museum/c-mogul.html |archive-date=16 May 2008 }}</ref> रूपिया [[भारत]] संघे [[नेपाल]], [[इंडोनेशिया]], [[मालदीव]], [[मॉरिशस]], [[पाकिस्तान]] आ [[श्रीलंका]] के मुद्रा बाटे। सोना के सिक्का मुहर कहात रहे आ होकर भार 169 ग्राम होखत रहे आ तामा के सिक्का के पईसा कहल जात रहे।
शेर शाह [[जी.टी.रोड]] के फेर से बनवलन आ होके राहे आजम कहलन जवन अफगान से बांग्लादेश के जोड़त रहे।
==छबि सभ==
<gallery>
Lal Darwaza or Sher Shah Gate, with ruins along approach.jpg|लाल दरवाजा
Rohtas Fort Magnificent Kabuli Gate.jpg|रोहतास के किला के काबुली दुआर
Qila-i-Kohna.jpg|किलाई कुहना महजीद, 1541 मे शेर शाह सूरी के बनवावल
Sher Mandal, Purana Qila.jpg|शेर शाह के ईयाद मे मुगल सभ के बनवावल शेर मंडल
Copper Dam of Sher Shah Suri, issued from Narnul mint.jpg|नारनौल टक्साल के तामा के दाम
</gallery>
==सनर्भ==
[[श्रेणी:भारत के इतिहास]]
[[श्रेणी:मध्यकालीन भारत]]
dvw0lpxk7x05bkr6vxr6ae19dysjqa0
Module:Separated entries
828
38938
803567
634468
2018-12-17T20:54:33Z
en>Amorymeltzer
0
Changed protection level for "[[Module:Separated entries]]": [[WP:High-risk templates|High-risk Lua module]]: Over 2M transclusions ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite))
634467
Scribunto
text/plain
-- This module takes positional parameters as input and concatenates them with
-- an optional separator. The final separator (the "conjunction") can be
-- specified independently, enabling natural-language lists like
-- "foo, bar, baz and qux". The starting parameter can also be specified.
local compressSparseArray = require('Module:TableTools').compressSparseArray
local p = {}
function p._main(args)
local separator = args.separator
-- Decode (convert to Unicode) HTML escape sequences, such as " " for space.
and mw.text.decode(args.separator) or ''
local conjunction = args.conjunction and mw.text.decode(args.conjunction) or separator
-- Discard values before the starting parameter.
local start = tonumber(args.start)
if start then
for i = 1, start - 1 do args[i] = nil end
end
-- Discard named parameters.
local values = compressSparseArray(args)
return mw.text.listToText(values, separator, conjunction)
end
local function makeInvokeFunction(separator, conjunction, first)
return function (frame)
local args = require('Module:Arguments').getArgs(frame)
args.separator = separator or args.separator
args.conjunction = conjunction or args.conjunction
args.first = first or args.first
return p._main(args)
end
end
p.main = makeInvokeFunction()
p.br = makeInvokeFunction('<br />')
p.comma = makeInvokeFunction(mw.message.new('comma-separator'):plain())
return p
r3n53qz4zycs3vxyo5ms9qjo1jm32bp
803568
803567
2024-11-09T16:07:13Z
en>Pppery
0
Copy from sandbox per request
803568
Scribunto
text/plain
-- This module takes positional parameters as input and concatenates them with
-- an optional separator. The final separator (the "conjunction") can be
-- specified independently, enabling natural-language lists like
-- "foo, bar, baz and qux". The starting parameter can also be specified.
local compressSparseArray = require('Module:TableTools').compressSparseArray
local p = {}
function p._main(args)
local dataPlural = args.dataPlural
local separator = args.separator
-- Decode (convert to Unicode) HTML escape sequences, such as " " for space.
and mw.text.decode(args.separator) or ''
local conjunction = args.conjunction and mw.text.decode(args.conjunction) or separator
-- Discard values before the starting parameter.
local start = tonumber(args.start)
if start then
for i = 1, start - 1 do args[i] = nil end
end
-- Discard named parameters.
local values = compressSparseArray(args)
local result = mw.text.listToText(values, separator, conjunction)
if dataPlural and #values > 0 and (#separator > 0 or #conjunction > 0) then
-- flag to [[Module:Detect singular]] whether this is a list with >1 element
result = result..'<span style="display:none" data-plural="'..(#values > 1 and '1' or '0')..'"></span>'
end
return result
end
local function makeInvokeFunction(separator, conjunction, first)
return function (frame)
local args = require('Module:Arguments').getArgs(frame)
args.separator = separator or args.separator
args.conjunction = conjunction or args.conjunction
args.first = first or args.first
return p._main(args)
end
end
p.main = makeInvokeFunction()
p.br = makeInvokeFunction('<br />')
p.comma = makeInvokeFunction(mw.message.new('comma-separator'):plain())
return p
0tp4y1kozxhpcw1ykqbo8e2gq604ky9
803569
803568
2025-10-13T16:33:18Z
en>Ahecht
0
{frameOnly = (frame.args.frameOnly or '') ~= ''}
803569
Scribunto
text/plain
-- This module takes positional parameters as input and concatenates them with
-- an optional separator. The final separator (the "conjunction") can be
-- specified independently, enabling natural-language lists like
-- "foo, bar, baz and qux". The starting parameter can also be specified.
local compressSparseArray = require('Module:TableTools').compressSparseArray
local p = {}
function p._main(args)
local dataPlural = args.dataPlural
local separator = args.separator
-- Decode (convert to Unicode) HTML escape sequences, such as " " for space.
and mw.text.decode(args.separator) or ''
local conjunction = args.conjunction and mw.text.decode(args.conjunction) or separator
-- Discard values before the starting parameter.
local start = tonumber(args.start)
if start then
for i = 1, start - 1 do args[i] = nil end
end
-- Discard named parameters.
local values = compressSparseArray(args)
local result = mw.text.listToText(values, separator, conjunction)
if dataPlural and #values > 0 and (#separator > 0 or #conjunction > 0) then
-- flag to [[Module:Detect singular]] whether this is a list with >1 element
result = result..'<span style="display:none" data-plural="'..(#values > 1 and '1' or '0')..'"></span>'
end
return result
end
local function makeInvokeFunction(separator, conjunction, first)
return function (frame)
local args = require('Module:Arguments').getArgs(frame, {frameOnly = (frame.args.frameOnly or '') ~= ''})
args.separator = separator or args.separator
args.conjunction = conjunction or args.conjunction
args.first = first or args.first
return p._main(args)
end
end
p.main = makeInvokeFunction()
p.br = makeInvokeFunction('<br />')
p.comma = makeInvokeFunction(mw.message.new('comma-separator'):plain())
return p
d3q6xup28j3xpu77wa8xwjjm6j0xizq
803570
803569
2026-07-30T00:17:49Z
SM7
3953
3 revisions imported from [[:en:Module:Separated_entries]]
803569
Scribunto
text/plain
-- This module takes positional parameters as input and concatenates them with
-- an optional separator. The final separator (the "conjunction") can be
-- specified independently, enabling natural-language lists like
-- "foo, bar, baz and qux". The starting parameter can also be specified.
local compressSparseArray = require('Module:TableTools').compressSparseArray
local p = {}
function p._main(args)
local dataPlural = args.dataPlural
local separator = args.separator
-- Decode (convert to Unicode) HTML escape sequences, such as " " for space.
and mw.text.decode(args.separator) or ''
local conjunction = args.conjunction and mw.text.decode(args.conjunction) or separator
-- Discard values before the starting parameter.
local start = tonumber(args.start)
if start then
for i = 1, start - 1 do args[i] = nil end
end
-- Discard named parameters.
local values = compressSparseArray(args)
local result = mw.text.listToText(values, separator, conjunction)
if dataPlural and #values > 0 and (#separator > 0 or #conjunction > 0) then
-- flag to [[Module:Detect singular]] whether this is a list with >1 element
result = result..'<span style="display:none" data-plural="'..(#values > 1 and '1' or '0')..'"></span>'
end
return result
end
local function makeInvokeFunction(separator, conjunction, first)
return function (frame)
local args = require('Module:Arguments').getArgs(frame, {frameOnly = (frame.args.frameOnly or '') ~= ''})
args.separator = separator or args.separator
args.conjunction = conjunction or args.conjunction
args.first = first or args.first
return p._main(args)
end
end
p.main = makeInvokeFunction()
p.br = makeInvokeFunction('<br />')
p.comma = makeInvokeFunction(mw.message.new('comma-separator'):plain())
return p
d3q6xup28j3xpu77wa8xwjjm6j0xizq
प्रयोगकर्ता वार्ता:LasseKeskinen
3
41099
803284
567581
2026-07-29T13:26:34Z
Ternarius
20240
Ternarius पन्ना [[प्रयोगकर्ता वार्ता:Roopeank]] के [[प्रयोगकर्ता वार्ता:LasseKeskinen]] पर स्थानांतरण कइलें: प्रयोगकर्ता के नाँव बदलाव के दौरान पन्ना "[[Special:CentralAuth/Roopeank|Roopeank]]" से "[[Special:CentralAuth/LasseKeskinen|LasseKeskinen]]" पर ऑटोमेटिक रूप से स्थानांतरित भइल।
567581
wikitext
text/x-wiki
{| id="GeoPort-upper" width="100%" cellpadding="5" cellspacing="6" style="background:#FFFAFF; text-align: justify; border-style:ridge; border-width:1px; border-color:#A9A9A9;"
|-
|<div style="float:right;width:30%"><small>तुरंत भइल बदलाव देखीं</small><br/> [[File:View recent changes.svg|45px|link=विशेष:RecentChanges]]<br/></div>
<h1 style="display:inline-block;margin-top:.1em; text-align:right; margin-bottom:.2em; border-bottom:0; font-weight:bold;">Welcome! स्वागतम्! [[File:Crystal Clear app ksmiletris.png|25px]] </h1>
राउर बहुत-बहुत स्वागत बा '''{{BASEPAGENAME}}''' जी ! {{#if: | {{{1}}} | }}<br/>
<div style="float:right; <!--background:#F5F5DC;--> width:30%">
<!-- दाहिना साइडबार -->
<big>'''ई जरूर पढ़ल जाय:'''</big>
{| border="5" cellspacing="10" cellpadding="5" height="50" align=center border=0 style="background: transparent"
|-
| style="background-color:#FFFFFF; border: solid 2px #FFFFFF; padding:10px 20px;" | [[विकिपीडिया:विकिपीडिया का ना हवे|विकिपीडिया का ना हवे?]]
|-
| style="background-color:#FFFFFF border: solid 2px #FFFFFF; padding:10px 20px;" | [[विकिपीडिया:भोजपुरी में कइसे टाइप करब?|भोजपुरी में टाइपिंग]]
|-
| style="background-color:#FFFFFF; border: solid 1px #FFFFFF; padding:10px 20px;" | [[विकिपीडिया:सत्यापन जोग|प्रमाणित बात लिखीं]]
|-
| style="background-color:#FFFFFF; border: solid 1px #FFFFFF; padding:10px 20px;" | [[मदद:फुटनोट|संदर्भ कइसे जोड़ीं?]]
|-
| style="background-color:#FFFFFF; border: solid 1px #FFFFFF; padding:10px 20px;" | [[विकिपीडिया:नीति अउरी दिसानिर्देस|विकिनीति आ निर्देश]]
|}</div>
<!-- मुख्य पाठ -->
'''{{#if: | {{{1}}} | {{BASEPAGENAME}} }} जी''',
एह समय रउँआ [[विकिमीडिया फाउन्डेशन]] के परियोजना [[भोजपुरी]] [[विकिपीडिया]] पर बाड़ीं। भोजपुरी विकिपीडिया एगो मुक्त डिजिटल [[ज्ञानकोश]] हवे, जेवन अइसन भइया-बहिनी लोग मिल के लिखले बा जे ज्ञान बाँटे में बिस्वास करत बाटे। एह समय ए परियोजना में [[विशेष:ActiveUsers|{{NUMBEROFUSERS}} सदस्य]] लोग शामिल बाटे। ई बहुते खुशी क बाति बा कि रउँओं ए में शामिल हो गइल बाड़ीं।
* पहिले से बनल [[विकिपीडिया:लेख|लेखवन]] में कौनो संपादन खाली टेस्ट करे खातिर मत करीं। कौनों तरह के परीक्षण <small>(प्रयोग या टेस्टिंग)</small> [[विकिपीडिया:अभ्यास पन्ना|अभ्यास पन्ना]] या [[Special:MyPage/sandbox|अपना अभ्यास पन्ना]] पर करीं।
* [[विकिपीडिया:आपन परिचय कइसे देईं?|आपन परिचय]] आप संछेप में [[प्रयोगकर्ता:{{BASEPAGENAME}}|अपना सदस्य पन्ना]] पर दे सकत बानी। बहुत पर्सनल बात इहाँ मत लिखीं, न कौनों परचार वाली बात लिखीं। अपने खुद के बारे में लेख मत बनाईं।
* दुसरा [[विकिपीडिया:चौपाल |सदस्य लोगन से बात]] करत समय, [[मदद:वार्ता पन्ना|बातचीत पन्ना]] पर सनेसा लिखले की बाद आपन [[विकिपीडिया:दसखत|दसखत]] <small>(हस्ताक्षर)</small> जरूर करीं। एकरा खातिर अंत में चार गो टेढ़का डैश (<nowiki>~~~~</nowiki>) लिख देंईं या टूलबार में [[File:Insert-signature2.svg|link=|alt=]] पर क्लिक करीं।
* मदद चाहत होखीं त विकिपीडिया के [[विकिपीडिया:मदद|मदद पन्ना]] पर जाईं।
<!-- फुटर के कड़ी सभ -->
सीखे-समझे खातिर कुछ अउरी कड़ी नीचे दिहल जात बाटे:
{| border="5" cellspacing="1" cellpadding="0" height="50" align=center border=0 style="background: transparent"
| style="background-color:#FFFFFF; border: solid 2px #F2BDCD; padding:1px 10px;" | [[विकिपीडिया:स्वशिक्षा|शुरू से सीखीं]]
|
| style="background-color:#FFFFFF; border: solid 2px #F2BDCD; padding:1px 20px;" | [[मदद:संपादन|संपादन सीखीं]]
|
| style="background-color:#FFFFFF; border: solid 2px #F2BDCD; padding:1px 20px;" | [[विकिपीडिया:नया लेख कइसे सुरू करीं?|नया लेख]]
| style="background-color:#FFFFFF; border: solid 2px #F2BDCD; padding:1px 20px;" | [[विकिपीडिया:अइसन लेख मना बाटे|लेख मनाहीं]]
|
| style="background-color:#FFFFFF; border: solid 2px #F2BDCD; padding:1px 20px;" | [[विकिपीडिया:पंचशील|पंचशील]]
| style="background-color:#FFFFFF; border: solid 2px #F2BDCD; padding:1px 20px;" | [[विकिपीडिया:समुदाय पोर्टल|सदस्य समाज पन्ना]]
|}
|}
-- [[प्रयोगकर्ता:नया सदस्य स्वागतकर्ता|नया सदस्य स्वागतकर्ता]] ([[प्रयोगकर्ता वार्ता:नया सदस्य स्वागतकर्ता|बात करीं]]) ०६:२९, २८ मार्च २०१७ (UTC)
5zpw1grl8axk28magx1qppe20lhjmuo
इंटरसिटी एक्सप्रेस
0
46266
803343
774664
2026-07-29T16:22:14Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803343
wikitext
text/x-wiki
'''इंटरसिटी एक्सप्रेस''' भारत में क एक्सप्रेस-ट्रेन सेवा ह जवन की प्रमुख रेलवे जंक्शन और राज्य की राजधानियन के जोड़ेला।
अन्य एक्सप्रेस गाड़ीन की तुलना में ए गाड़ीन में लागत कम बा और आमतौर प 5-6 घंटा की भीतर अदमी अपनी गंतव्य ले पहुंच जाला। आमतौर प इ एके दिन में एक दौर क यात्रा पूरा क ले ली स, राती में अपनी मूल स्थान जहाँ से चले ली स ओइजा लौट आवेली स और एमें केवल एक रेक का प्रयोग कइल जाला। आम तौर प, एमें केवल बैठक और एगो पेंट्री कार होला, इंटरसिटी एक्सप्रेस रेलगाड़ी रोजाना चलेली स, सिवाय की:
[[जबलपुर]] - हबीबगंज इंटरसिटी एक्सप्रेस दैनिक
राजगढ़ इंटरसिटी एक्सप्रेस, राजगीर → दानापुर इंटरसिटी
इंदौर - जबलपुर इंटरसिटी एक्सप्रेस, सप्ताह में तीन दिन
भुवनेश्वर-विशाखापटनम इंटरसिटी एक्सप्रेस, दैनिक
भोपाल - ग्वालियर इंटरसिटी एक्सप्रेस, सप्ताह में पांच दिन
इंदौर - हज़रत निजामुद्दीन इंटरसिटी एक्सप्रेस, जेके 550 किलोमीटर (340 मील) मार्ग के कवर कइले में 11 घंटा लागेला। इ गाड़ी दुगो रैक क इस्तेमाल करेले और एमें अनारक्षित, स्लीपर और 3एसी कोच भी होला।
बेंगलुरु-एर्नाकुलम इंटरसिटी एक्सप्रेस में भी 2 रैक क उपयोग होला।
भुवनेश्वर - राउरकेला इंटरसिटी एक्सप्रेस, दैनिक
कोयम्बटूर - मंगलूरु इंटरसिटी एक्सप्रेस में 2 रैक उपयोग होला और एके एर्नाद एक्सप्रेस से साझा कइल जाला।
हल्दीबारी-कोलकाता इंटरसिटी एक्सप्रेस त्रि-साप्ताहिक 11 घंटा 10 मिनट में 630 किलोमीटर क सफ़र तय करेले। एमें चेयर कार और एसी चेयर कार कोच हो न स। इ गाड़ी सुपरफास्ट श्रेणी क ह।
कटिहार-पटना इंटरसिटी रविवार को छोड़के बाकी दिन चलेले
==इंटरसिटी-एक्सप्रेस गाड़ी अलग-अलग नांव से:==
* तिरुवनंतपुरम-गुरुवायूर एक्सप्रेस
* ताज एक्सप्रेस
* कोवाई एक्सप्रेस
* ब्रिंडन एक्सप्रेस
* लाल बाग एक्सप्रेस
* पिनकिनी एक्सप्रेस
* सतावहाना एक्सप्रेस
* जन्मभूमि एक्सप्रेस
* पल्लवन एक्सप्रेस
* विंध्याचल एक्सप्रेस
* टिपू एक्सप्रेस
* चामुंडी एक्सप्रेस
* प्रगति एक्सप्रेस
* डेक्कन रानी एक्सप्रेस
* डेक्कन एक्सप्रेस
* वनाद एक्सप्रेस
* वनचिनाद एक्सप्रेस
* वैगई एक्सप्रेस
* अमृता एक्सप्रेस
* शताब्दी एक्सप्रेस
* जन शताब्दी एक्सप्रेस
* एर्नाकुलम / अलापी
* हुतात्मा एक्सप्रेस
==मार्ग==
इंटरसिटी एक्सप्रेस मार्ग (जबले बतावल न जा टेबल राउंड ट्रिप मनाई):
* नई दिल्ली-कानपुर
* आगरा कैंट नई दिल्ली इंटरसिटी एक्सप्रेस
* नई दिल्ली-चंडीगढ़ (एक तरफ)
* देहरादून-नई दिल्ली (एक तरफ)
* नई दिल्ली-ग्वालियर
* मुंबई पुणे इंटरसिटी एक्सप्रेस
* [[मुंबई]]-नागपुर
* अकोला - हैदराबाद कचेगुडा इंटरसिटी एक्सप्रेस
* नागपुर अजनी - अमरावती - नागपुर अजनी इंटरसिटी एक्सप्रेस
* जयपुर-[[जोधपुर]] इंटरसिटी एक्सप्रेस
* भोपाल - इंदौर इंटरसिटी एक्सप्रेस
* भोपाल-ग्वालियर इंटरसिटी एक्सप्रेस
* भोपाल - जबलपुर इंटरसिटी एक्सप्रेस
* बांद्रा सूरत इंटरसिटी एक्सप्रेस
* सूरत जामनगर इंटरसिटी एक्सप्रेस
* लखनऊ-[[गोरखपुर]]
* भोपाल-दाहोद
* भोपाल-जयपुर एक्सप्रेस
* भुवनेश्वर-विशाखापत्तनम इंटरसिटी एक्सप्रेस
* इंदौर-ग्वालियर इंटरसिटी एक्सप्रेस
* इंदौर-भिंड एक्सप्रेस
* इंदौर - हज़रत निजामुद्दीन इंटरसिटी एक्सप्रेस
* इंदौर-जोधपुर रणथंभौर एक्सप्रेस
* इंदौर-उदयपुर वीर भूमि एक्सप्रेस
* ग्वालियर-[[उदयपुर]]
* जबलपुर-रीवा इंटरसिटी एक्सप्रेस
* रतलाम-अजमेर
* बैंगलोर-हुबली सिद्दगंगा इंटरसिटी एक्सप्रेस
* भुवनेश्वर-राउरकेला इंटरसिटी एक्सप्रेस
* [[चेन्नई]] कोयम्बटूर इंटरसिटी एक्सप्रेस
* बैंगलोर-शिमोगा टाउन इंटरसिटी एक्सप्रेस
* एरनाकुलम जंक्शन-तिरुवनंतपुरम वनचिनाद एक्सप्रेस
* [[तिरुवनंतपुरम]]-गुरुवायूर इंटरसिटी एक्सप्रेस
* तिरुचिरापल्ली-तिरुनेलवेली इंटरसिटी एक्सप्रेस
* [[पटना]]-डेहरी ऑन सोन इंटरसिटी एक्सप्रेस
* [[गुवाहाटी]]-लेडो इंटरसिटी एक्सप्रेस
* रांची-डेहरी ऑन सोन इंटरसिटी एक्सप्रेस
* एरनाकुलम जंक्शन - कन्नूर मेन इंटरसिटी एक्सप्रेस
* कोयम्बटूर जेएन - मैंगलोर सेंट्रल इंटरसिटी एक्सप्रेस
* कटिहार-सिलीगुड़ी इंटरसिटी एक्सप्रेस
* कामाख्या - डिब्रूगढ़ इंटरसिटी एक्सप्रेस
* सिलीगुड़ी-धुबरी इंटरसिटी एक्सप्रेस
* हावड़ा मालदा टाउन इंटरसिटी एक्सप्रेस
* अलीपुरद्वार-लुमडिंग इंटरसिटी एक्सप्रेस
* न्यू जलपाईगुड़ी-बालूरघाट इंटरसिटी एक्सप्रेस
* पटना-धनबाद इंटरसिटी एक्सप्रेस
* साहिबगंज-दानापुर इंटरसिटी एक्सप्रेस
* [[वाराणसी]] लखनऊ इंटरसिटी एक्सप्रेस<ref>https://erail.in/14219-bsb-lko-exp</ref>
* पुरी-संबलपुर इंटरसिटी एक्सप्रेस
* अलीपुरद्वार-न्यू जलपाईगुड़ी इंटरसिटी एक्सप्रेस
* कामाख्या-डकारगांव इंटरसिटी एक्सप्रेस
* जोधपुर-इंदौर इंटरसिटी एक्सप्रेस<ref>{{cite web |url=https://www.cleartrip.com/trains/12466 |title=Intercity Express 12466 Time Table |publisher=cleartrip.com |date= |accessdate=18 May 2017 |archive-date=20 July 2016 |archive-url=https://web.archive.org/web/20160720113343/http://www.cleartrip.com/trains/12466 |url-status=dead }}</ref>
* जयनगर-दानापुर इंटरसिटी एक्सप्रेस
* [[लखनऊ]]-आगरा इंटरसिटी एक्सप्रेस
* भबुआ-आरा-पटना इंटरसिटी एक्सप्रेस
* कानपुर- प्रतापगढ़ इंटरसिटी एक्सप्रेस<ref>http://indiarailinfo.com/train/kanpur-pratapgarh-intercity-express-14124-cnb-to-lko/1268/452/336</ref>
* वलसाड-दाहोद इंटरसिटी एक्सप्रेस
* [[पुणे]] सोलापुर इंटरसिटी एक्सप्रेस
==संदर्भ==
[[श्रेणी:भारतीय रेल के गाड़ी]]
hdfrllcrw7xhkckj8eeb2ucd33gk51j
टेम्पलेट:Wikidata
10
46668
803642
467173
2017-09-16T18:52:24Z
en>JohnBlackburne
0
Nominated for merging with [[Template:Any qp]]; see [[Wikipedia:Templates for discussion/Log/2017 September 16#Template:Any qp]]. ([[WP:TW|TW]])
803642
wikitext
text/x-wiki
{{Tfm/dated|page=Wikidata|otherpage=Any qp|link=Wikipedia:Templates for discussion/Log/2017 September 16#Template:Wikidata|type=tiny|help=off|bigbox={{#invoke:Noinclude|noinclude|text=yes}}}}
<includeonly>{{safesubst:#invoke:Wd|main}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
qxdmvsfwl5gviv5d8h5rjnrmovy0u1m
803643
803642
2017-09-16T18:54:08Z
en>Mike Peel
0
Wrap Tfm in noinclude to prevent it from messing up articles
803643
wikitext
text/x-wiki
<noinclude>{{Tfm/dated|page=Wikidata|otherpage=Any qp|link=Wikipedia:Templates for discussion/Log/2017 September 16#Template:Wikidata|type=tiny|help=off|bigbox={{#invoke:Noinclude|noinclude|text=yes}}}}</noinclude>
<includeonly>{{safesubst:#invoke:Wd|main}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
olsy61s9wu0jagycthesow17aa07mez
803644
803643
2017-09-16T21:15:41Z
en>Was a bee
0
remove carriage return which generate errors
803644
wikitext
text/x-wiki
<noinclude>{{Tfm/dated|page=Wikidata|otherpage=Any qp|link=Wikipedia:Templates for discussion/Log/2017 September 16#Template:Wikidata|type=tiny|help=off|bigbox={{#invoke:Noinclude|noinclude|text=yes}}}}</noinclude><includeonly>{{safesubst:#invoke:Wd|main}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
6806pqmtwnpqqjbz0gbwpepto0jvd1o
803645
803644
2017-09-30T12:38:55Z
en>Godric on Leave
0
Updating TFD template: discussion was relisted ([[WP:XFDC|XFDcloser]])
803645
wikitext
text/x-wiki
<noinclude>{{Tfm/dated|page=Wikidata|otherpage=Any qp|link=Wikipedia:Templates for discussion/Log/2017 September 30#Template:Wikidata|type=tiny|help=off|bigbox={{#invoke:Noinclude|noinclude|text=yes}}}}</noinclude><includeonly>{{safesubst:#invoke:Wd|main}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
pn02jo2dfc993jrpuksslmfv7lsp0gy
803646
803645
2017-10-09T16:54:32Z
en>MusikAnimal
0
Protected "[[Template:Wikidata]]": [[WP:High-risk templates|Highly visible template]]; thousands of transclusions ([Edit=Require autoconfirmed or confirmed access] (indefinite))
803645
wikitext
text/x-wiki
<noinclude>{{Tfm/dated|page=Wikidata|otherpage=Any qp|link=Wikipedia:Templates for discussion/Log/2017 September 30#Template:Wikidata|type=tiny|help=off|bigbox={{#invoke:Noinclude|noinclude|text=yes}}}}</noinclude><includeonly>{{safesubst:#invoke:Wd|main}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
pn02jo2dfc993jrpuksslmfv7lsp0gy
803647
803646
2017-10-10T02:15:36Z
en>Primefac
0
TFD closed with no action taken to this template
803647
wikitext
text/x-wiki
<includeonly>{{safesubst:#invoke:Wd|main}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
aqnmh4azo4jle51xny4knl3b1hl9873
803648
803647
2018-02-23T16:31:07Z
en>Primefac
0
Changed protection level for "[[Template:Wikidata]]": [[WP:HIGHRISK|high-risk]] template with 4000+ transclusions ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
803647
wikitext
text/x-wiki
<includeonly>{{safesubst:#invoke:Wd|main}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
aqnmh4azo4jle51xny4knl3b1hl9873
803649
803648
2018-03-05T12:34:02Z
en>Primefac
0
Changed protection level for "[[Template:Wikidata]]": to match module ([Edit=Require extended confirmed access] (indefinite) [Move=Require extended confirmed access] (indefinite))
803647
wikitext
text/x-wiki
<includeonly>{{safesubst:#invoke:Wd|main}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
aqnmh4azo4jle51xny4knl3b1hl9873
803650
803649
2019-07-03T10:25:41Z
en>Ymblanter
0
Changed protection level for "[[Template:Wikidata]]": [[WP:High-risk templates|Highly visible template]]: request at [[WP:RFPP]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
803647
wikitext
text/x-wiki
<includeonly>{{safesubst:#invoke:Wd|main}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
aqnmh4azo4jle51xny4knl3b1hl9873
803651
803650
2026-07-30T00:21:31Z
SM7
3953
9 revisions imported from [[:en:Template:Wikidata]]
803647
wikitext
text/x-wiki
<includeonly>{{safesubst:#invoke:Wd|main}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
aqnmh4azo4jle51xny4knl3b1hl9873
Module:Wd
828
46671
803508
661806
2019-11-09T11:10:16Z
en>Thayts
0
Minor improvements
803508
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for i = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for i, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
local lang
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.getEntity(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label, lang = mw.wikibase.getLabelWithLang(id)
-- don't allow language fallback
if lang ~= self.langCode then
label = nil
end
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
return mw.getCurrentFrame():extensionTag("math", datavalue)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getEntity(globe):getLabel("en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for i, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
local now = os.date('!*t')
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
elseif endTime == " " then
-- end time is 'unknown', assume it is somewhere in the past;
-- we can do this by taking the current date as a placeholder for the end time
endTimeY = now['year']
endTimeM = now['month']
endTimeD = now['day']
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(now['year'], now['month'], now['day'], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(now['year'], now['month'], now['day'], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(now['year'], now['month'], now['day'], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(now['year'], now['month'], now['day'], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for i, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self.conf:getValue(claim.mainsnak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for i, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for i, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for i, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for i2, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
function getEntityId(arg, allowOmitPropPrefix)
if not arg then
return nil
elseif arg:sub(1,1):upper() == "Q" then
return arg:upper() -- entity ID of an item was given
elseif arg:sub(1,9):lower() == "property:" then
return replaceAlias(mw.text.trim(arg:sub(10))):upper() -- entity ID of a property was given
elseif allowOmitPropPrefix and arg ~= "" then
return replaceAlias(arg):upper() -- could be an entity ID of a property without "Property:" prefix
else
return ""
end
end
function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
function claimCommand(args, funcName)
local _ = Config:new()
_:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- process flags and commands
repeat
lastArg = nextArg(args)
until not _:processFlagOrCommand(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, false)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
else
lastArg = nextArg(args)
end
_.entity = mw.wikibase.getEntity(_.entityID)
_.propertyID = replaceAlias(lastArg)
if not _.entity or not _.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
_.propertyID = _.propertyID:upper()
if not _.entity.claims or not _.entity.claims[_.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = _.entity.claims[_.propertyID]
if _.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > _.states.qualifiersCount then
-- claim ID or literal value has been given
_.propertyValue = nextArg(args)
end
for i = 1, _.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
_.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif _.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
_.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if _.propertyValue then
_.propertyValue = replaceSpecialChars(_.propertyValue)
if _.propertyValue ~= "" and mw.text.trim(_.propertyValue) == "" then
_.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
_.propertyValue = mw.text.trim(_.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif _.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if _.states.qualifiersCount > 0 and not _.states[parameters.property] then
_.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if _.states[parameters.reference] and not _.states[parameters.property] and _.states.qualifiersCount == 0
and not _.states[parameters.reference].rawValue then
_.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if _.states.qualifiersCount == 1 then
_.separators["sep"..parameters.qualifier] = _.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(_.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and _.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not _.states[parameters.property] then
_.states[parameters.property] = State:new(_)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if _.singleClaim then
_.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if _.sourcedOnly and not _.states[parameters.reference] then
_:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
_:setFormatAndSeparators(_.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to _.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
_.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = _:concatValues(_.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if _.editable and value ~= "" then
value = value .. _:getEditIcon()
end
return value
end
function generalCommand(args, funcName)
local _ = Config:new()
_.curState = State:new(_)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not _:processFlag(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, true)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
end
_.entity = mw.wikibase.getEntity(_.entityID)
if not _.entity then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = _:getLabel(_.entityID, _.curState.rawValue, _.curState.linked, _.curState.shortName)
elseif funcName == p.generalCommands.title then
_.inSitelinks = true
if _.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(_.entityID)
end
if _.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
if _.entity.descriptions[_.langCode] then
value = _.entity.descriptions[_.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
_.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not _.entity.aliases or not _.entity.aliases[_.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = _.entity.aliases[_.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not _.entity.sitelinks or not _.entity.sitelinks[_.siteID] or not _.entity.sitelinks[_.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = _.entity.sitelinks[_.siteID].badges
_.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(badges, hooks))
end
end
value = value or ""
if _.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. _:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
function establishCommands(commandList, commandFunc)
for commandIndex, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args, i, v
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
j103xrm4tflt2pu79hh2ftk2ciash91
803509
803508
2019-11-09T15:29:58Z
en>Thayts
0
Added new named argument 'date'
803509
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for i = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for i, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
local lang
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.getEntity(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label, lang = mw.wikibase.getLabelWithLang(id)
-- don't allow language fallback
if lang ~= self.langCode then
label = nil
end
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
return mw.getCurrentFrame():extensionTag("math", datavalue)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getEntity(globe):getLabel("en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for i, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for i, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self.conf:getValue(claim.mainsnak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for i, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for i, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for i, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for i2, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
function getEntityId(arg, allowOmitPropPrefix)
if not arg then
return nil
elseif arg:sub(1,1):upper() == "Q" then
return arg:upper() -- entity ID of an item was given
elseif arg:sub(1,9):lower() == "property:" then
return replaceAlias(mw.text.trim(arg:sub(10))):upper() -- entity ID of a property was given
elseif allowOmitPropPrefix and arg ~= "" then
return replaceAlias(arg):upper() -- could be an entity ID of a property without "Property:" prefix
else
return ""
end
end
function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
function claimCommand(args, funcName)
local _ = Config:new()
_:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
_.atDate = {parseDate(args[p.args.date])}
_.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not _:processFlagOrCommand(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, false)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
else
lastArg = nextArg(args)
end
_.entity = mw.wikibase.getEntity(_.entityID)
_.propertyID = replaceAlias(lastArg)
if not _.entity or not _.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
_.propertyID = _.propertyID:upper()
if not _.entity.claims or not _.entity.claims[_.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = _.entity.claims[_.propertyID]
if _.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > _.states.qualifiersCount then
-- claim ID or literal value has been given
_.propertyValue = nextArg(args)
end
for i = 1, _.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
_.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif _.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
_.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if _.propertyValue then
_.propertyValue = replaceSpecialChars(_.propertyValue)
if _.propertyValue ~= "" and mw.text.trim(_.propertyValue) == "" then
_.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
_.propertyValue = mw.text.trim(_.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif _.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if _.states.qualifiersCount > 0 and not _.states[parameters.property] then
_.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if _.states[parameters.reference] and not _.states[parameters.property] and _.states.qualifiersCount == 0
and not _.states[parameters.reference].rawValue then
_.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if _.states.qualifiersCount == 1 then
_.separators["sep"..parameters.qualifier] = _.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(_.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and _.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not _.states[parameters.property] then
_.states[parameters.property] = State:new(_)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if _.singleClaim then
_.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if _.sourcedOnly and not _.states[parameters.reference] then
_:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
_:setFormatAndSeparators(_.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to _.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
_.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = _:concatValues(_.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if _.editable and value ~= "" then
value = value .. _:getEditIcon()
end
return value
end
function generalCommand(args, funcName)
local _ = Config:new()
_.curState = State:new(_)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not _:processFlag(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, true)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
end
_.entity = mw.wikibase.getEntity(_.entityID)
if not _.entity then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = _:getLabel(_.entityID, _.curState.rawValue, _.curState.linked, _.curState.shortName)
elseif funcName == p.generalCommands.title then
_.inSitelinks = true
if _.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(_.entityID)
end
if _.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
if _.entity.descriptions[_.langCode] then
value = _.entity.descriptions[_.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
_.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not _.entity.aliases or not _.entity.aliases[_.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = _.entity.aliases[_.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not _.entity.sitelinks or not _.entity.sitelinks[_.siteID] or not _.entity.sitelinks[_.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = _.entity.sitelinks[_.siteID].badges
_.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(badges, hooks))
end
end
value = value or ""
if _.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. _:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
function establishCommands(commandList, commandFunc)
for commandIndex, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args, i, v
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
mv3hj1bx2thaaovyw6wd40d5fsl62aw
803510
803509
2019-11-11T20:04:39Z
en>Thayts
0
Added validity check for entity ID
803510
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for i = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for i, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
local lang
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.getEntity(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label, lang = mw.wikibase.getLabelWithLang(id)
-- don't allow language fallback
if lang ~= self.langCode then
label = nil
end
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
return mw.getCurrentFrame():extensionTag("math", datavalue)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getEntity(globe):getLabel("en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for i, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for i, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self.conf:getValue(claim.mainsnak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for i, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for i, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for i, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for i2, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
function getEntityId(arg, allowOmitPropPrefix)
if not arg then
return nil
end
if arg:sub(1,9):lower() == "property:" then
arg = replaceAlias(mw.text.trim(arg:sub(10))) -- entity ID of a property was given
if arg:sub(1,1):upper() ~= "P" then
return ""
end
elseif allowOmitPropPrefix then
arg = replaceAlias(arg) -- could be an entity ID of a property without "Property:" prefix
elseif arg:sub(1,1):upper() ~= "Q" then
return ""
end
if not mw.wikibase.isValidEntityId(arg) then
return ""
end
return arg:upper()
end
function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
function claimCommand(args, funcName)
local _ = Config:new()
_:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
_.atDate = {parseDate(args[p.args.date])}
_.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not _:processFlagOrCommand(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, false)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
else
lastArg = nextArg(args)
end
_.entity = mw.wikibase.getEntity(_.entityID)
_.propertyID = replaceAlias(lastArg)
if not _.entity or not _.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
_.propertyID = _.propertyID:upper()
if not _.entity.claims or not _.entity.claims[_.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = _.entity.claims[_.propertyID]
if _.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > _.states.qualifiersCount then
-- claim ID or literal value has been given
_.propertyValue = nextArg(args)
end
for i = 1, _.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
_.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif _.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
_.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if _.propertyValue then
_.propertyValue = replaceSpecialChars(_.propertyValue)
if _.propertyValue ~= "" and mw.text.trim(_.propertyValue) == "" then
_.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
_.propertyValue = mw.text.trim(_.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif _.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if _.states.qualifiersCount > 0 and not _.states[parameters.property] then
_.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if _.states[parameters.reference] and not _.states[parameters.property] and _.states.qualifiersCount == 0
and not _.states[parameters.reference].rawValue then
_.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if _.states.qualifiersCount == 1 then
_.separators["sep"..parameters.qualifier] = _.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(_.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and _.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not _.states[parameters.property] then
_.states[parameters.property] = State:new(_)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if _.singleClaim then
_.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if _.sourcedOnly and not _.states[parameters.reference] then
_:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
_:setFormatAndSeparators(_.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to _.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
_.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = _:concatValues(_.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if _.editable and value ~= "" then
value = value .. _:getEditIcon()
end
return value
end
function generalCommand(args, funcName)
local _ = Config:new()
_.curState = State:new(_)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not _:processFlag(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, true)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
end
_.entity = mw.wikibase.getEntity(_.entityID)
if not _.entity then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = _:getLabel(_.entityID, _.curState.rawValue, _.curState.linked, _.curState.shortName)
elseif funcName == p.generalCommands.title then
_.inSitelinks = true
if _.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(_.entityID)
end
if _.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
if _.entity.descriptions[_.langCode] then
value = _.entity.descriptions[_.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
_.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not _.entity.aliases or not _.entity.aliases[_.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = _.entity.aliases[_.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not _.entity.sitelinks or not _.entity.sitelinks[_.siteID] or not _.entity.sitelinks[_.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = _.entity.sitelinks[_.siteID].badges
_.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(badges, hooks))
end
end
value = value or ""
if _.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. _:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
function establishCommands(commandList, commandFunc)
for commandIndex, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args, i, v
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
6sbowg0r10rkc0g4n4lt3vfp0y27cyf
803511
803510
2019-11-16T16:23:22Z
en>Thayts
0
Minor performance improvement
803511
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for i = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for i, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
local lang
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.getEntity(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label, lang = mw.wikibase.getLabelWithLang(id)
-- don't allow language fallback
if lang ~= self.langCode then
label = nil
end
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
return mw.getCurrentFrame():extensionTag("math", datavalue)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getEntity(globe):getLabel("en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for i, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for i, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self.conf:getValue(claim.mainsnak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for i, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for i, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for i, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for i2, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
function getEntityId(arg, allowOmitPropPrefix)
if not arg then
return nil
end
if arg:sub(1,9):lower() == "property:" then
arg = replaceAlias(mw.text.trim(arg:sub(10))) -- entity ID of a property was given
if arg:sub(1,1):upper() ~= "P" then
return ""
end
elseif allowOmitPropPrefix then
arg = replaceAlias(arg) -- could be an entity ID of a property without "Property:" prefix
elseif arg:sub(1,1):upper() ~= "Q" then
return ""
end
if not mw.wikibase.isValidEntityId(arg) then
return ""
end
return arg:upper()
end
function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
function claimCommand(args, funcName)
local _ = Config:new()
_:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
_.atDate = {parseDate(args[p.args.date])}
_.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not _:processFlagOrCommand(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, false)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
else
lastArg = nextArg(args)
end
_.entity = mw.wikibase.getEntity(_.entityID)
_.propertyID = replaceAlias(lastArg)
if not _.entity or not _.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
_.propertyID = _.propertyID:upper()
if not _.entity.claims or not _.entity.claims[_.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = _.entity.claims[_.propertyID]
if _.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > _.states.qualifiersCount then
-- claim ID or literal value has been given
_.propertyValue = nextArg(args)
end
for i = 1, _.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
_.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif _.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
_.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if _.propertyValue then
_.propertyValue = replaceSpecialChars(_.propertyValue)
if _.propertyValue ~= "" and mw.text.trim(_.propertyValue) == "" then
_.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
_.propertyValue = mw.text.trim(_.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif _.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if _.states.qualifiersCount > 0 and not _.states[parameters.property] then
_.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if _.states[parameters.reference] and not _.states[parameters.property] and _.states.qualifiersCount == 0
and not _.states[parameters.reference].rawValue then
_.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if _.states.qualifiersCount == 1 then
_.separators["sep"..parameters.qualifier] = _.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(_.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and _.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not _.states[parameters.property] then
_.states[parameters.property] = State:new(_)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if _.singleClaim then
_.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if _.sourcedOnly and not _.states[parameters.reference] then
_:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
_:setFormatAndSeparators(_.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to _.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
_.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = _:concatValues(_.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if _.editable and value ~= "" then
value = value .. _:getEditIcon()
end
return value
end
function generalCommand(args, funcName)
local _ = Config:new()
_.curState = State:new(_)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not _:processFlag(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, true)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
end
if not _.entityID then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = _:getLabel(_.entityID, _.curState.rawValue, _.curState.linked, _.curState.shortName)
elseif funcName == p.generalCommands.title then
_.inSitelinks = true
if _.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(_.entityID)
end
if _.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
_.entity = mw.wikibase.getEntity(_.entityID)
if _.entity.descriptions[_.langCode] then
value = _.entity.descriptions[_.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
_.entity = mw.wikibase.getEntity(_.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
_.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not _.entity.aliases or not _.entity.aliases[_.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = _.entity.aliases[_.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not _.entity.sitelinks or not _.entity.sitelinks[_.siteID] or not _.entity.sitelinks[_.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = _.entity.sitelinks[_.siteID].badges
_.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(badges, hooks))
end
end
value = value or ""
if _.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. _:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
function establishCommands(commandList, commandFunc)
for commandIndex, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args, i, v
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
rnz4vmitjjn00oi2n5ze633f7jmzr4w
803512
803511
2019-11-17T11:01:52Z
en>Thayts
0
Minor performance improvements
803512
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for i = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for i, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
return mw.getCurrentFrame():extensionTag("math", datavalue)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for i, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for i, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self.conf:getValue(claim.mainsnak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for i, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for i, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for i, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for i2, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
function getEntityId(arg, allowOmitPropPrefix)
if not arg then
return nil
end
if arg:sub(1,9):lower() == "property:" then
arg = replaceAlias(mw.text.trim(arg:sub(10))) -- entity ID of a property was given
if arg:sub(1,1):upper() ~= "P" then
return ""
end
elseif allowOmitPropPrefix then
arg = replaceAlias(arg) -- could be an entity ID of a property without "Property:" prefix
elseif arg:sub(1,1):upper() ~= "Q" then
return ""
end
if not mw.wikibase.isValidEntityId(arg) then
return ""
end
return arg:upper()
end
function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
function claimCommand(args, funcName)
local _ = Config:new()
_:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
_.atDate = {parseDate(args[p.args.date])}
_.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not _:processFlagOrCommand(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, false)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
else
lastArg = nextArg(args)
end
_.entity = mw.wikibase.getEntity(_.entityID)
_.propertyID = replaceAlias(lastArg)
if not _.entity or not _.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
_.propertyID = _.propertyID:upper()
if not _.entity.claims or not _.entity.claims[_.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = _.entity.claims[_.propertyID]
if _.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > _.states.qualifiersCount then
-- claim ID or literal value has been given
_.propertyValue = nextArg(args)
end
for i = 1, _.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
_.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif _.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
_.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if _.propertyValue then
_.propertyValue = replaceSpecialChars(_.propertyValue)
if _.propertyValue ~= "" and mw.text.trim(_.propertyValue) == "" then
_.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
_.propertyValue = mw.text.trim(_.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif _.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if _.states.qualifiersCount > 0 and not _.states[parameters.property] then
_.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if _.states[parameters.reference] and not _.states[parameters.property] and _.states.qualifiersCount == 0
and not _.states[parameters.reference].rawValue then
_.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if _.states.qualifiersCount == 1 then
_.separators["sep"..parameters.qualifier] = _.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(_.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and _.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not _.states[parameters.property] then
_.states[parameters.property] = State:new(_)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if _.singleClaim then
_.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if _.sourcedOnly and not _.states[parameters.reference] then
_:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
_:setFormatAndSeparators(_.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to _.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
_.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = _:concatValues(_.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if _.editable and value ~= "" then
value = value .. _:getEditIcon()
end
return value
end
function generalCommand(args, funcName)
local _ = Config:new()
_.curState = State:new(_)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not _:processFlag(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, true)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
end
if not _.entityID then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = _:getLabel(_.entityID, _.curState.rawValue, _.curState.linked, _.curState.shortName)
elseif funcName == p.generalCommands.title then
_.inSitelinks = true
if _.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(_.entityID)
end
if _.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
_.entity = mw.wikibase.getEntity(_.entityID)
if _.entity.descriptions[_.langCode] then
value = _.entity.descriptions[_.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
_.entity = mw.wikibase.getEntity(_.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
_.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not _.entity.aliases or not _.entity.aliases[_.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = _.entity.aliases[_.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not _.entity.sitelinks or not _.entity.sitelinks[_.siteID] or not _.entity.sitelinks[_.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = _.entity.sitelinks[_.siteID].badges
_.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(badges, hooks))
end
end
value = value or ""
if _.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. _:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
function establishCommands(commandList, commandFunc)
for commandIndex, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args, i, v
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
jakrtj7pz4fq6h7as9dp85pn6odsn4p
803513
803512
2019-11-17T11:36:16Z
en>Thayts
0
Bug fix
803513
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for i = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for i, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
return mw.getCurrentFrame():extensionTag("math", datavalue)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for i, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for i, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self.conf:getValue(claim.mainsnak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for i, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for i, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for i, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for i2, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
function getEntityId(arg, allowOmitPropPrefix)
if not arg then
return nil
end
if arg:sub(1,9):lower() == "property:" then
arg = replaceAlias(mw.text.trim(arg:sub(10))) -- entity ID of a property was given
if arg:sub(1,1):upper() ~= "P" then
return ""
end
elseif allowOmitPropPrefix then
arg = replaceAlias(arg) -- could be an entity ID of a property without "Property:" prefix
elseif arg:sub(1,1):upper() ~= "Q" then
return ""
end
if not mw.wikibase.isValidEntityId(arg) then
return ""
end
return arg:upper()
end
function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
function claimCommand(args, funcName)
local _ = Config:new()
_:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
_.atDate = {parseDate(args[p.args.date])}
_.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not _:processFlagOrCommand(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, false)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
else
lastArg = nextArg(args)
end
_.entity = mw.wikibase.getEntity(_.entityID)
_.propertyID = replaceAlias(lastArg)
if not _.entity or not _.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
_.propertyID = _.propertyID:upper()
if not _.entity.claims or not _.entity.claims[_.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = _.entity.claims[_.propertyID]
if _.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > _.states.qualifiersCount then
-- claim ID or literal value has been given
_.propertyValue = nextArg(args)
end
for i = 1, _.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
_.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif _.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
_.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if _.propertyValue then
_.propertyValue = replaceSpecialChars(_.propertyValue)
if _.propertyValue ~= "" and mw.text.trim(_.propertyValue) == "" then
_.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
_.propertyValue = mw.text.trim(_.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif _.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif _.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if _.states.qualifiersCount > 0 and not _.states[parameters.property] then
_.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if _.states[parameters.reference] and not _.states[parameters.property] and _.states.qualifiersCount == 0
and not _.states[parameters.reference].rawValue then
_.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if _.states.qualifiersCount == 1 then
_.separators["sep"..parameters.qualifier] = _.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(_.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and _.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not _.states[parameters.property] then
_.states[parameters.property] = State:new(_)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if _.singleClaim then
_.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if _.sourcedOnly and not _.states[parameters.reference] then
_:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
_:setFormatAndSeparators(_.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to _.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
_.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = _:concatValues(_.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if _.editable and value ~= "" then
value = value .. _:getEditIcon()
end
return value
end
function generalCommand(args, funcName)
local _ = Config:new()
_.curState = State:new(_)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not _:processFlag(lastArg)
-- use the entity ID from the positional arguments if given
_.entityID = getEntityId(lastArg, true)
if not _.entityID or _.entityID == "" then
-- if no positional ID is given, use the eid argument if given
_.entityID = getEntityId(args[p.args.eid], true)
if _.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not _.entityID then
-- by default, use the item-entity connected to the current page
_.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
end
if not _.entityID or not mw.wikibase.entityExists(_.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = _:getLabel(_.entityID, _.curState.rawValue, _.curState.linked, _.curState.shortName)
elseif funcName == p.generalCommands.title then
_.inSitelinks = true
if _.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(_.entityID)
end
if _.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
_.entity = mw.wikibase.getEntity(_.entityID)
if _.entity.descriptions[_.langCode] then
value = _.entity.descriptions[_.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
_.entity = mw.wikibase.getEntity(_.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
_.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not _.entity.aliases or not _.entity.aliases[_.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = _.entity.aliases[_.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not _.entity.sitelinks or not _.entity.sitelinks[_.siteID] or not _.entity.sitelinks[_.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = _.entity.sitelinks[_.siteID].badges
_.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
-- iterate to collect values
value = _:concatValues(_.curState:iterate(badges, hooks))
end
end
value = value or ""
if _.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. _:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
function establishCommands(commandList, commandFunc)
for commandIndex, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args, i, v
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
3tqxff3fmdrg8t7kxnn93wow7eeyj1a
803514
803513
2019-11-20T21:54:27Z
en>Thayts
0
Replaced underscore variables per talk page
803514
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for i = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for i, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
return mw.getCurrentFrame():extensionTag("math", datavalue)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for i, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for i, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self.conf:getValue(claim.mainsnak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for i, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for i, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for i, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for i2, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
function getEntityId(arg, allowOmitPropPrefix)
if not arg then
return nil
end
if arg:sub(1,9):lower() == "property:" then
arg = replaceAlias(mw.text.trim(arg:sub(10))) -- entity ID of a property was given
if arg:sub(1,1):upper() ~= "P" then
return ""
end
elseif allowOmitPropPrefix then
arg = replaceAlias(arg) -- could be an entity ID of a property without "Property:" prefix
elseif arg:sub(1,1):upper() ~= "Q" then
return ""
end
if not mw.wikibase.isValidEntityId(arg) then
return ""
end
return arg:upper()
end
function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- use the entity ID from the positional arguments if given
cfg.entityID = getEntityId(lastArg, false)
if not cfg.entityID or cfg.entityID == "" then
-- if no positional ID is given, use the eid argument if given
cfg.entityID = getEntityId(args[p.args.eid], true)
if cfg.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not cfg.entityID then
-- by default, use the item-entity connected to the current page
cfg.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
else
lastArg = nextArg(args)
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
cfg.propertyID = replaceAlias(lastArg)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- use the entity ID from the positional arguments if given
cfg.entityID = getEntityId(lastArg, true)
if not cfg.entityID or cfg.entityID == "" then
-- if no positional ID is given, use the eid argument if given
cfg.entityID = getEntityId(args[p.args.eid], true)
if cfg.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not cfg.entityID then
-- by default, use the item-entity connected to the current page
cfg.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
end
if not cfg.entityID or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if cfg.entity.descriptions[cfg.langCode] then
value = cfg.entity.descriptions[cfg.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
function establishCommands(commandList, commandFunc)
for commandIndex, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args, i, v
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
8l7cql13ballf651eg25sb2ppf8gu1i
803515
803514
2019-11-23T12:58:03Z
en>Thayts
0
803515
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
local function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
return mw.getCurrentFrame():extensionTag("math", datavalue)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self.conf:getValue(claim.mainsnak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, allowOmitPropPrefix)
if not arg then
return nil
end
if arg:sub(1,9):lower() == "property:" then
arg = replaceAlias(mw.text.trim(arg:sub(10))) -- entity ID of a property was given
if arg:sub(1,1):upper() ~= "P" then
return ""
end
elseif allowOmitPropPrefix then
arg = replaceAlias(arg) -- could be an entity ID of a property without "Property:" prefix
elseif arg:sub(1,1):upper() ~= "Q" then
return ""
end
if not mw.wikibase.isValidEntityId(arg) then
return ""
end
return arg:upper()
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- use the entity ID from the positional arguments if given
cfg.entityID = getEntityId(lastArg, false)
if not cfg.entityID or cfg.entityID == "" then
-- if no positional ID is given, use the eid argument if given
cfg.entityID = getEntityId(args[p.args.eid], true)
if cfg.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not cfg.entityID then
-- by default, use the item-entity connected to the current page
cfg.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
else
lastArg = nextArg(args)
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
cfg.propertyID = replaceAlias(lastArg)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- use the entity ID from the positional arguments if given
cfg.entityID = getEntityId(lastArg, true)
if not cfg.entityID or cfg.entityID == "" then
-- if no positional ID is given, use the eid argument if given
cfg.entityID = getEntityId(args[p.args.eid], true)
if cfg.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not cfg.entityID then
-- by default, use the item-entity connected to the current page
cfg.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
end
if not cfg.entityID or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if cfg.entity.descriptions[cfg.langCode] then
value = cfg.entity.descriptions[cfg.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
2xomx38gx67wbrc4djychq19i0w3qky
803516
803515
2020-01-17T14:52:37Z
en>Thayts
0
Added 'qid' attribute for math tags per request at talk
803516
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
local function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, isProperty)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (isProperty or self.propertyID == p.aliasesP.hasPart) and snak.property == p.aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self.conf:getValue(claim.mainsnak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, true)} -- create one value object; isProperty = true
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, allowOmitPropPrefix)
if not arg then
return nil
end
if arg:sub(1,9):lower() == "property:" then
arg = replaceAlias(mw.text.trim(arg:sub(10))) -- entity ID of a property was given
if arg:sub(1,1):upper() ~= "P" then
return ""
end
elseif allowOmitPropPrefix then
arg = replaceAlias(arg) -- could be an entity ID of a property without "Property:" prefix
elseif arg:sub(1,1):upper() ~= "Q" then
return ""
end
if not mw.wikibase.isValidEntityId(arg) then
return ""
end
return arg:upper()
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- use the entity ID from the positional arguments if given
cfg.entityID = getEntityId(lastArg, false)
if not cfg.entityID or cfg.entityID == "" then
-- if no positional ID is given, use the eid argument if given
cfg.entityID = getEntityId(args[p.args.eid], true)
if cfg.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not cfg.entityID then
-- by default, use the item-entity connected to the current page
cfg.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
else
lastArg = nextArg(args)
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
cfg.propertyID = replaceAlias(lastArg)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- use the entity ID from the positional arguments if given
cfg.entityID = getEntityId(lastArg, true)
if not cfg.entityID or cfg.entityID == "" then
-- if no positional ID is given, use the eid argument if given
cfg.entityID = getEntityId(args[p.args.eid], true)
if cfg.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not cfg.entityID then
-- by default, use the item-entity connected to the current page
cfg.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
end
if not cfg.entityID or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if cfg.entity.descriptions[cfg.langCode] then
value = cfg.entity.descriptions[cfg.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
d9nvllyn4rtpqoojjox16qkwzl8zdgg
803517
803516
2020-01-18T10:59:51Z
en>Thayts
0
Moved function 'getValue' to State to be able to get its type and added type check for math tags
803517
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
local function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (self.type == parameters.property or (self.conf.propertyID == p.aliasesP.hasPart and self.type:sub(1,2) == parameters.qualifier)) and snak.property == p.aliasesP.definingFormula then
attribute = {qid = self.conf.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.conf.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self.conf:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.conf.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.conf.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.conf.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self.conf:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function State:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function State:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function State:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function State:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.conf.periods[1] and self.conf.periods[2] and self.conf.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.conf.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.conf.atDate[1], self.conf.atDate[2], self.conf.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.conf.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.conf.atDate[1], self.conf.atDate[2], self.conf.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.conf.atDate[1], self.conf.atDate[2], self.conf.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.conf.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.conf.atDate[1], self.conf.atDate[2], self.conf.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, allowOmitPropPrefix)
if not arg then
return nil
end
if arg:sub(1,9):lower() == "property:" then
arg = replaceAlias(mw.text.trim(arg:sub(10))) -- entity ID of a property was given
if arg:sub(1,1):upper() ~= "P" then
return ""
end
elseif allowOmitPropPrefix then
arg = replaceAlias(arg) -- could be an entity ID of a property without "Property:" prefix
elseif arg:sub(1,1):upper() ~= "Q" then
return ""
end
if not mw.wikibase.isValidEntityId(arg) then
return ""
end
return arg:upper()
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- use the entity ID from the positional arguments if given
cfg.entityID = getEntityId(lastArg, false)
if not cfg.entityID or cfg.entityID == "" then
-- if no positional ID is given, use the eid argument if given
cfg.entityID = getEntityId(args[p.args.eid], true)
if cfg.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not cfg.entityID then
-- by default, use the item-entity connected to the current page
cfg.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
else
lastArg = nextArg(args)
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
cfg.propertyID = replaceAlias(lastArg)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- use the entity ID from the positional arguments if given
cfg.entityID = getEntityId(lastArg, true)
if not cfg.entityID or cfg.entityID == "" then
-- if no positional ID is given, use the eid argument if given
cfg.entityID = getEntityId(args[p.args.eid], true)
if cfg.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not cfg.entityID then
-- by default, use the item-entity connected to the current page
cfg.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
end
if not cfg.entityID or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if cfg.entity.descriptions[cfg.langCode] then
value = cfg.entity.descriptions[cfg.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
elq9sykeolm2h8sxtxmmd1dzw4llshl
803518
803517
2020-01-18T19:25:42Z
en>Thayts
0
Cleaner solution, partly undoing previous edit
803518
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
local function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == p.aliasesP.hasPart)) and snak.property == p.aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, allowOmitPropPrefix)
if not arg then
return nil
end
if arg:sub(1,9):lower() == "property:" then
arg = replaceAlias(mw.text.trim(arg:sub(10))) -- entity ID of a property was given
if arg:sub(1,1):upper() ~= "P" then
return ""
end
elseif allowOmitPropPrefix then
arg = replaceAlias(arg) -- could be an entity ID of a property without "Property:" prefix
elseif arg:sub(1,1):upper() ~= "Q" then
return ""
end
if not mw.wikibase.isValidEntityId(arg) then
return ""
end
return arg:upper()
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- use the entity ID from the positional arguments if given
cfg.entityID = getEntityId(lastArg, false)
if not cfg.entityID or cfg.entityID == "" then
-- if no positional ID is given, use the eid argument if given
cfg.entityID = getEntityId(args[p.args.eid], true)
if cfg.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not cfg.entityID then
-- by default, use the item-entity connected to the current page
cfg.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
else
lastArg = nextArg(args)
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
cfg.propertyID = replaceAlias(lastArg)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- use the entity ID from the positional arguments if given
cfg.entityID = getEntityId(lastArg, true)
if not cfg.entityID or cfg.entityID == "" then
-- if no positional ID is given, use the eid argument if given
cfg.entityID = getEntityId(args[p.args.eid], true)
if cfg.entityID == "" then
-- if eid was explicitly set to empty, then this returns an empty string
return ""
elseif not cfg.entityID then
-- by default, use the item-entity connected to the current page
cfg.entityID = mw.wikibase.getEntityIdForCurrentPage()
end
end
if not cfg.entityID or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if cfg.entity.descriptions[cfg.langCode] then
value = cfg.entity.descriptions[cfg.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
bh9mwh8i2dr0jt8vezp4ieyslhm22zf
803519
803518
2020-02-01T16:26:53Z
en>Thayts
0
Added new named argument 'page'
803519
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
local function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == p.aliasesP.hasPart)) and snak.property == p.aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang then
return datavalue['text'], datavalue['language']
elseif datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if cfg.entity.descriptions[cfg.langCode] then
value = cfg.entity.descriptions[cfg.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
kca4kbz167j11gju3a59pq3rwa1u0if
803520
803519
2020-04-18T22:39:18Z
en>Thayts
0
Copy from sandbox: removed language return value for monolingualtext which is no longer used; made explicit check on singular commands (per edit request)
803520
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
local function loadSubmodules(frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{p.aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == p.aliasesP.hasPart)) and snak.property == p.aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{p.aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[p.aliasesP.importedFrom] then
statement.snaks[p.aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[p.aliasesP.inferredFrom] then
statement.snaks[p.aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[p.aliasesP.typeOfReference] then
statement.snaks[p.aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[p.aliasesP.image] then
statement.snaks[p.aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, p.aliasesP.language) == self.conf.langName then
statement.snaks[p.aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] or params[p.aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[p.aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
params[p.aliasesP.title] = nil
params[p.aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if cfg.entity.descriptions[cfg.langCode] then
value = cfg.entity.descriptions[cfg.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadSubmodules(frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadSubmodules(frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
catghfkahlnvvds3za0wpqvy1l24vzg
803521
803520
2020-04-19T10:39:11Z
en>Thayts
0
Assimilating aliasesP back into the main module. IMPORTANT UPDATE INSTRUCTIONS: In order to prevent errors while updating this module on your local wiki, follow these steps: #1. Edit your Module:Wd/i18n and add the line "function p.init() return p end" right before the last line (above "return p"); #2. Copy the contents of [[:en:Module:Wd]] to your local module; #3. Copy the contents of [[:en:Module:Wd/i18n]] to your local submodule; #4. You can safely delete Module:Wd/aliasesP now.
803521
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/aliasesP]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n")
i18n = i18n.init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if cfg.entity.descriptions[cfg.langCode] then
value = cfg.entity.descriptions[cfg.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
j761o05rl6453pzayi53ibb0pmpqfcn
803522
803521
2020-04-19T10:41:20Z
en>Thayts
0
803522
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n")
i18n = i18n.init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if cfg.entity.descriptions[cfg.langCode] then
value = cfg.entity.descriptions[cfg.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
h92g70uxhopoqrswthpm2zvgqs0shuf
803523
803522
2020-04-19T10:41:33Z
en>Thayts
0
803523
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n")
i18n = i18n.init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increase this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if cfg.entity.descriptions[cfg.langCode] then
value = cfg.entity.descriptions[cfg.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
eiuoystn7fr17k1kc2eylks1ln1n37n
803524
803523
2020-04-19T11:10:43Z
en>Thayts
0
ADDITIONAL NOTE: Copy the structure of [[:en:Module:Wd/i18n]], but of course leave your local translations intact!
803524
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n")
i18n = i18n.init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.sitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if cfg.entity.descriptions[cfg.langCode] then
value = cfg.entity.descriptions[cfg.langCode].value
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
gdo1m2obqkrimz2yglllqsl7z8r4wy4
803525
803524
2020-04-25T18:00:06Z
en>Thayts
0
Updated to latest Wikibase API
803525
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n")
i18n = i18n.init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
6tc8vi8zz1ronmbudwfya9yxfw09sa3
803526
803525
2020-04-25T18:23:22Z
en>Thayts
0
Minor simplification
803526
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
d676bdka4cnkgqhgqc73ti65xl5mngq
803527
803526
2020-09-12T00:15:17Z
en>IceWelder
0
Avoid Lua errors should Wikidata not be available (tested via sandbox & testcases)
803527
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
tr2dkpn9ogdr62y68xlj6n0hv4e1vs6
803528
803527
2022-02-18T18:41:24Z
en>Uzume
0
escape raw label text pulled from Wikidata, so it cannot be treated as wikitext markup when used in wikitext links
803528
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Property:"
end
end
if title then
label = buildWikilink(prefix .. title, mw.text.nowiki(label)) -- escape raw text so it cannot be wikitext markup
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
52q9z6uhox53miuowkafz4s2obgkybr
803529
803528
2022-02-18T19:15:39Z
en>Uzume
0
always return an wikilink for Wikidata items when requested; even when there is no applicable sitelink available: link to Wikidata
803529
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
prefix = "d:" .. prefix
title = label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
if not title then
title = id
prefix = "d:Special:EntityPage/"
end
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
title = id
prefix = "d:Special:EntityPage/"
end
end
if title then
label = buildWikilink(prefix .. title, mw.text.nowiki(label)) -- escape raw text so it cannot be wikitext markup
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
82rz8jow90gl55vst4p18vx8owt8spq
803530
803529
2022-02-21T22:33:11Z
en>Uzume
0
cmd label|linked: stop linking to Wikidata items when there is no local sitelink (as per [[Module talk:Wd#Linking non-notable people to Wikidata entry]]); also allow raw links to any Wikidata entity (e.g., including lexeme which do not have multilingual labels like items and properties have)
803530
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
if mw.wikibase.getSitelink(id) then
prefix, title = "d:Special:GoToLinkedPage/"..cfg.siteID.."/", id
else
--[=[ no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id]=]
end
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
if title then
label = buildWikilink(prefix .. title, mw.text.nowiki(label)) -- escape raw text so it cannot be wikitext markup
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
b2whedqtye60hkzks5g4y6yii7sf7wg
803531
803530
2022-02-21T22:35:04Z
en>Uzume
0
fix
803531
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
if mw.wikibase.getSitelink(id) then
prefix, title = "d:Special:GoToLinkedPage/"..mw.wikibase.getGlobalSiteId().."/", id
else
--[=[ no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id]=]
end
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
if title then
label = buildWikilink(prefix .. title, mw.text.nowiki(label)) -- escape raw text so it cannot be wikitext markup
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
m1878bsf7qqtcok3l4og768jw8b3st4
803532
803531
2022-02-22T20:58:50Z
en>Uzume
0
always escape raw label from Wikidata when link is requested—even when there is no local sitelink and a wikitext link is not returned; raw label is still returned when link is not requested
803532
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
if mw.wikibase.getSitelink(id) then
prefix, title = "d:Special:GoToLinkedPage/"..self.siteID.."/", id
else
--[=[ no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id]=]
end
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
976p68fkvzv3x07i255x91si2id25w9
803533
803532
2022-02-23T17:08:07Z
en>Thayts
0
Partial revert to undo introduction of linking obscurity
803533
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local version = 1 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
citeParams['default'] = {}
-- start by adding authors up front
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
value = value .. "."
end
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
9scru16s5146chaxiyatugrhsfa43zn
803534
803533
2023-05-06T16:54:09Z
en>Pppery
0
Per edit request
803534
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 2 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
62oz0m09z2nkf3lw8vq0i8sco64lmgw
803535
756519
2023-06-15T20:42:15Z
en>Uzume
0
import [[voy:Special:Diff/4022511/prev]] tools.wmflabs.org/geohack -> geohack.toolforge.org
803535
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 2 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
19mwvad9pmi1jhxk6mxi8djpqnzqt9i
803536
803535
2023-06-15T21:00:50Z
en>Uzume
0
localize "copyTable"
803536
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 2 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
qi54p6abr2d5xohpgnpwxd4yqc5da7z
803537
803536
2023-06-15T21:01:37Z
en>Uzume
0
Undid revision 1160335980 by [[Special:Contributions/Uzume|Uzume]] ([[User talk:Uzume|talk]])
803537
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 2 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
19mwvad9pmi1jhxk6mxi8djpqnzqt9i
803538
803537
2023-06-15T21:02:24Z
en>Uzume
0
Undid revision 1160336072 by [[Special:Contributions/Uzume|Uzume]] ([[User talk:Uzume|talk]])
803538
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 2 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
qi54p6abr2d5xohpgnpwxd4yqc5da7z
803539
803538
2023-06-15T21:05:13Z
en>Uzume
0
fix localization
803539
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local copyTable
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 2 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
b90xwzph43y66ax840m91vyrd58qo5c
803540
803539
2023-06-15T21:09:49Z
en>Uzume
0
localize "parseDate"
803540
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local copyTable, parseDate
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 2 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
bvcqtiyo2u0mbs3q06ygw9jn26d8iih
803541
803540
2023-06-15T21:14:07Z
en>Uzume
0
strict
803541
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local copyTable, parseDate -- forward declarations
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 2 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
itbdurbcp43v78pdi053pmexne9l5dj
803542
756520
2023-06-16T02:55:10Z
en>Uzume
0
move definition of module locals before their usage
803542
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 2 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
th2j80ge3l6rbyuhcnk7nb4a3tkqn5f
803543
803542
2023-06-16T02:55:45Z
en>Uzume
0
Undid revision 1160374075 by [[Special:Contributions/Uzume|Uzume]] ([[User talk:Uzume|talk]])
803543
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local copyTable, parseDate -- forward declarations
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 2 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
itbdurbcp43v78pdi053pmexne9l5dj
803544
803543
2023-06-16T03:04:32Z
en>Uzume
0
reorder local module definitions
803544
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 2 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
dmx757zjxwjs3gyqisdta3qnyngdqd1
803545
756521
2023-06-24T01:54:55Z
en>Pppery
0
Per edit request
803545
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 3 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "Wikimedia import URL"
if statement.snaks[aliasesP.wikimediaImportURL] then
statement.snaks[aliasesP.wikimediaImportURL] = nil
-- don't include "retrieved" if no "referenceURL" is present,
-- as "retrieved" probably belongs to "Wikimedia import URL"
if statement.snaks[aliasesP.retrieved] and not statement.snaks[aliasesP.referenceURL] then
statement.snaks[aliasesP.retrieved] = nil
end
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
cs2sn2920oaaks2otf9hikheh86f6hy
803546
803545
2023-06-30T14:12:23Z
en>MSGJ
0
bug fixes by [[User:Janhrach]]
803546
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local version = 3 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "Wikimedia import URL"
if statement.snaks[aliasesP.wikimediaImportURL] then
statement.snaks[aliasesP.wikimediaImportURL] = nil
-- don't include "retrieved" if no "referenceURL" is present,
-- as "retrieved" probably belongs to "Wikimedia import URL"
if statement.snaks[aliasesP.retrieved] and not statement.snaks[aliasesP.referenceURL] then
statement.snaks[aliasesP.retrieved] = nil
end
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
6zuml6jvqcxm5u1sjjy487da1fohq8u
803547
803546
2023-07-05T16:41:33Z
en>Paine Ellsworth
0
per edit request on talk page - use "subject named as" as title only when a URL is present
803547
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local tempLink
local additionalRefProperties = {} -- will hold properties of the reference which are not in statement.snaks, namely backup title from "subject named as" and URL from an external ID
local wikidataPropertiesOfSource -- will contain "Wikidata property" properties of the item in stated in, if any
local version = 4 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "Wikimedia import URL"
if statement.snaks[aliasesP.wikimediaImportURL] then
statement.snaks[aliasesP.wikimediaImportURL] = nil
-- don't include "retrieved" if no "referenceURL" is present,
-- as "retrieved" probably belongs to "Wikimedia import URL"
if statement.snaks[aliasesP.retrieved] and not statement.snaks[aliasesP.referenceURL] then
statement.snaks[aliasesP.retrieved] = nil
end
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a URL from it and use the URL in the reference
-- find the "Wikidata property" properties in the item from "stated in"
wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
tempLink = self.conf:getValue(statement.snaks[wikidataPropertyOfSource][1], false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL.
additionalRefProperties[aliasesP.referenceURL] = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1") -- the URL is in wiki markup, so strip the square brackets and the display text
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalRefProperties[aliasesP.referenceURL]) then
additionalRefProperties[aliasesP.title] = statement.snaks[aliasesP.subjectNamedAs][1].datavalue.value
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- use additional properties
for i in pairs(additionalRefProperties) do
for j in pairs(citeParams) do
if not citeMismatch[j] and i18n["cite"][j][i] then
citeParams[j][i18n["cite"][j][i]] = additionalRefProperties[i]
else
citeMismatch[j] = true
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
nes30vv63ljt56gmmqn6e3gut2tls1k
803548
803547
2023-07-10T18:06:15Z
en>Pppery
0
Per edit request
803548
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local tempLink
local additionalRefProperties = {} -- will hold properties of the reference which are not in statement.snaks, namely backup title from "subject named as" and URL from an external ID
local wikidataPropertiesOfSource -- will contain "Wikidata property" properties of the item in stated in, if any
local version = 4 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "Wikimedia import URL"
if statement.snaks[aliasesP.wikimediaImportURL] then
statement.snaks[aliasesP.wikimediaImportURL] = nil
-- don't include "retrieved" if no "referenceURL" is present,
-- as "retrieved" probably belongs to "Wikimedia import URL"
if statement.snaks[aliasesP.retrieved] and not statement.snaks[aliasesP.referenceURL] then
statement.snaks[aliasesP.retrieved] = nil
end
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a URL from it and use the URL in the reference
-- find the "Wikidata property" properties in the item from "stated in"
wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
tempLink = self.conf:getValue(statement.snaks[wikidataPropertyOfSource][1], false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL.
additionalRefProperties[aliasesP.referenceURL] = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1") -- the URL is in wiki markup, so strip the square brackets and the display text
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalRefProperties[aliasesP.referenceURL]) then
additionalRefProperties[aliasesP.title] = statement.snaks[aliasesP.subjectNamedAs][1].datavalue.value
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- use additional properties
for i in pairs(additionalRefProperties) do
for j in pairs(citeParams) do
if not citeMismatch[j] and i18n["cite"][j][i] then
citeParams[j][i18n["cite"][j][i]] = additionalRefProperties[i]
else
citeMismatch[j] = true
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
jnrxu2rnvu16g4zg8thr6m4pwwr3r9p
803549
803548
2023-10-26T14:35:32Z
en>Jonesey95
0
Change error message color per talk page
803549
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local tempLink
local additionalRefProperties = {} -- will hold properties of the reference which are not in statement.snaks, namely backup title from "subject named as" and URL from an external ID
local wikidataPropertiesOfSource -- will contain "Wikidata property" properties of the item in stated in, if any
local version = 4 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "Wikimedia import URL"
if statement.snaks[aliasesP.wikimediaImportURL] then
statement.snaks[aliasesP.wikimediaImportURL] = nil
-- don't include "retrieved" if no "referenceURL" is present,
-- as "retrieved" probably belongs to "Wikimedia import URL"
if statement.snaks[aliasesP.retrieved] and not statement.snaks[aliasesP.referenceURL] then
statement.snaks[aliasesP.retrieved] = nil
end
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a URL from it and use the URL in the reference
-- find the "Wikidata property" properties in the item from "stated in"
wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
tempLink = self.conf:getValue(statement.snaks[wikidataPropertyOfSource][1], false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL.
additionalRefProperties[aliasesP.referenceURL] = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1") -- the URL is in wiki markup, so strip the square brackets and the display text
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalRefProperties[aliasesP.referenceURL]) then
additionalRefProperties[aliasesP.title] = statement.snaks[aliasesP.subjectNamedAs][1].datavalue.value
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- use additional properties
for i in pairs(additionalRefProperties) do
for j in pairs(citeParams) do
if not citeMismatch[j] and i18n["cite"][j][i] then
citeParams[j][i18n["cite"][j][i]] = additionalRefProperties[i]
else
citeMismatch[j] = true
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color:#dd3333\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
rbyyhay8jcfmk7t1lm1on7y4uwpt1fs
803550
803549
2023-10-26T14:36:14Z
en>Jonesey95
0
update version number per talk page
803550
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local tempLink
local additionalRefProperties = {} -- will hold properties of the reference which are not in statement.snaks, namely backup title from "subject named as" and URL from an external ID
local wikidataPropertiesOfSource -- will contain "Wikidata property" properties of the item in stated in, if any
local version = 5 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "Wikimedia import URL"
if statement.snaks[aliasesP.wikimediaImportURL] then
statement.snaks[aliasesP.wikimediaImportURL] = nil
-- don't include "retrieved" if no "referenceURL" is present,
-- as "retrieved" probably belongs to "Wikimedia import URL"
if statement.snaks[aliasesP.retrieved] and not statement.snaks[aliasesP.referenceURL] then
statement.snaks[aliasesP.retrieved] = nil
end
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a URL from it and use the URL in the reference
-- find the "Wikidata property" properties in the item from "stated in"
wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
tempLink = self.conf:getValue(statement.snaks[wikidataPropertyOfSource][1], false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL.
additionalRefProperties[aliasesP.referenceURL] = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1") -- the URL is in wiki markup, so strip the square brackets and the display text
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalRefProperties[aliasesP.referenceURL]) then
additionalRefProperties[aliasesP.title] = statement.snaks[aliasesP.subjectNamedAs][1].datavalue.value
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- use additional properties
for i in pairs(additionalRefProperties) do
for j in pairs(citeParams) do
if not citeMismatch[j] and i18n["cite"][j][i] then
citeParams[j][i18n["cite"][j][i]] = additionalRefProperties[i]
else
citeMismatch[j] = true
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color:#dd3333\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
4miawu63vlr3fz3fyhdaljxph8rodbo
803551
803550
2023-12-22T23:23:29Z
en>Pppery
0
Per edit request
803551
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local tempLink
local additionalRefProperties = {} -- will hold properties of the reference which are not in statement.snaks, namely backup title from "subject named as" and URL from an external ID
local wikidataPropertiesOfSource -- will contain "Wikidata property" properties of the item in stated in, if any
local version = 5 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "Wikimedia import URL"
if statement.snaks[aliasesP.wikimediaImportURL] then
statement.snaks[aliasesP.wikimediaImportURL] = nil
-- don't include "retrieved" if no "referenceURL" is present,
-- as "retrieved" probably belongs to "Wikimedia import URL"
if statement.snaks[aliasesP.retrieved] and not statement.snaks[aliasesP.referenceURL] then
statement.snaks[aliasesP.retrieved] = nil
end
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a URL from it and use the URL in the reference
-- find the "Wikidata property" properties in the item from "stated in"
wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
tempLink = self.conf:getValue(statement.snaks[wikidataPropertyOfSource][1], false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL.
additionalRefProperties[aliasesP.referenceURL] = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1") -- the URL is in wiki markup, so strip the square brackets and the display text
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalRefProperties[aliasesP.referenceURL]) then
additionalRefProperties[aliasesP.title] = statement.snaks[aliasesP.subjectNamedAs][1].datavalue.value
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- use additional properties
for i in pairs(additionalRefProperties) do
for j in pairs(citeParams) do
if not citeMismatch[j] and i18n["cite"][j][i] then
citeParams[j][i18n["cite"][j][i]] = additionalRefProperties[i]
else
citeMismatch[j] = true
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true) -- raw = true
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color:#dd3333\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
3cdxbtakyq00gfjfyegmimoqud2n0fc
803552
803551
2024-04-01T08:09:28Z
en>Janhrach
0
Update from sandbox, see talk
803552
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local tempLink
local additionalRefProperties = {} -- will hold properties of the reference which are not in statement.snaks, namely backup title from "subject named as" and link from an external ID
local wikidataPropertiesOfSource -- will contain "Wikidata property" properties of the item in stated in, if any
local version = 6 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "Wikimedia import URL"
if statement.snaks[aliasesP.wikimediaImportURL] then
statement.snaks[aliasesP.wikimediaImportURL] = nil
-- don't include "retrieved" if no "referenceURL" is present,
-- as "retrieved" probably belongs to "Wikimedia import URL"
if statement.snaks[aliasesP.retrieved] and not statement.snaks[aliasesP.referenceURL] then
statement.snaks[aliasesP.retrieved] = nil
end
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
tempLink = self.conf:getValue(statement.snaks[wikidataPropertyOfSource][1], false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL.
additionalRefProperties[aliasesP.referenceURL] = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1") -- the link is in wiki markup, so strip the square brackets and the display text
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalRefProperties[aliasesP.referenceURL]) then
additionalRefProperties[aliasesP.title] = statement.snaks[aliasesP.subjectNamedAs][1].datavalue.value
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
elseif i == aliasesP.statedIn then
-- Get "stated in" raw, as it is wanted (for Cite Q) even if it doesn't have a local language label.
params[aliasesP.statedIn] = {self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true)} -- raw = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, self.linked and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- use additional properties
for i in pairs(additionalRefProperties) do
for j in pairs(citeParams) do
if not citeMismatch[j] and i18n["cite"][j][i] then
citeParams[j][i18n["cite"][j][i]] = additionalRefProperties[i]
else
citeMismatch[j] = true
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
-- we need a processed "stated in" for this template
citeParams['web'][i18n['cite']['web'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, false, self.linked, true)
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color:#dd3333\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
6xowqousmlqn8snhh91c9p6t57np0h7
803553
803552
2024-06-10T19:17:03Z
en>Janhrach
0
Update from sandbox
803553
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local tempLink
local additionalRefProperties = {} -- will hold properties of the reference which are not in statement.snaks, namely backup title from "subject named as" and link from an external ID
local wikidataPropertiesOfSource -- will contain "Wikidata property" properties of the item in stated in, if any
local version = 7 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't include "imported from", which is added by a bot
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "Wikimedia import URL"
if statement.snaks[aliasesP.wikimediaImportURL] then
statement.snaks[aliasesP.wikimediaImportURL] = nil
-- don't include "retrieved" if no "referenceURL" is present,
-- as "retrieved" probably belongs to "Wikimedia import URL"
if statement.snaks[aliasesP.retrieved] and not statement.snaks[aliasesP.referenceURL] then
statement.snaks[aliasesP.retrieved] = nil
end
end
-- don't include "inferred from", which is added by a bot
if statement.snaks[aliasesP.inferredFrom] then
statement.snaks[aliasesP.inferredFrom] = nil
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
tempLink = self.conf:getValue(statement.snaks[wikidataPropertyOfSource][1], false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL.
additionalRefProperties[aliasesP.referenceURL] = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1") -- the link is in wiki markup, so strip the square brackets and the display text
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalRefProperties[aliasesP.referenceURL]) then
additionalRefProperties[aliasesP.title] = statement.snaks[aliasesP.subjectNamedAs][1].datavalue.value
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
elseif i == aliasesP.statedIn then
-- Get "stated in" raw, as it is wanted (for Cite Q) even if it doesn't have a local language label.
params[aliasesP.statedIn] = {self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true)} -- raw = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, self.linked and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- use additional properties
for i in pairs(additionalRefProperties) do
for j in pairs(citeParams) do
if not citeMismatch[j] and i18n["cite"][j][i] then
citeParams[j][i18n["cite"][j][i]] = additionalRefProperties[i]
else
citeMismatch[j] = true
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least "reference URL" is present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] then
-- we need a processed "stated in" for this template
citeParams['web'][i18n['cite']['web'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, false, self.linked, true)
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color:#dd3333\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
5fwn1sr79pjmu0sp9hvxy0aeb10hvca
803554
803553
2024-06-23T09:33:45Z
en>Janhrach
0
Update from sandbox – refs containing Wikimedia import URL, Inferred from or Imported from are ignored as a whole
803554
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local key, citeWeb, citeQ, label
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local numAuthorParameters = 0
local numAuthorNameStringParameters = 0
local tempLink
local additionalRefProperties = {} -- will hold properties of the reference which are not in statement.snaks, namely backup title from "subject named as" and link from an external ID
local wikidataPropertiesOfSource -- will contain "Wikidata property" properties of the item in stated in, if any
local version = 8 -- increment this each time the below logic is changed to avoid conflict errors
if statement.snaks then
-- don't use bot-added references referencing Wikimedia projects or containing "inferred from" (such references are not usable on Wikipedia)
if statement.snaks[aliasesP.importedFrom] or statement.snaks[aliasesP.wikimediaImportURL] or statement.snaks[aliasesP.inferredFrom] then
statement.snaks = {}
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
tempLink = self.conf:getValue(statement.snaks[wikidataPropertyOfSource][1], false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL.
additionalRefProperties[aliasesP.referenceURL] = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1") -- the link is in wiki markup, so strip the square brackets and the display text
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalRefProperties[aliasesP.referenceURL]) then
additionalRefProperties[aliasesP.title] = statement.snaks[aliasesP.subjectNamedAs][1].datavalue.value
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
elseif i == aliasesP.statedIn then
-- Get "stated in" raw, as it is wanted (for Cite Q) even if it doesn't have a local language label.
params[aliasesP.statedIn] = {self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true)} -- raw = true
else
params[i] = {self:getReferenceDetail(statement.snaks, i, false, self.linked and (statement.snaks[i][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #params[i] == 0 then
params[i] = nil
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
-- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
-- handle "author" and "author name string" specially if they use the same template parameter
elseif i == aliasesP.author or i == aliasesP.authorNameString then
if params[aliasesP.author] ~= nil then
numAuthorParameters = #params[aliasesP.author]
else
numAuthorParameters = 0
end
if params[aliasesP.authorNameString] ~= nil then
numAuthorNameStringParameters = #params[aliasesP.authorNameString]
else
numAuthorNameStringParameters = 0
end
-- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
-- reason: parameters are added to params each iteration of the loop, not before the loop
if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
for k=1, numAuthorParameters + numAuthorNameStringParameters do
if k <= numAuthorParameters then -- now handling the authors from the "author" property
citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
else -- now handling the authors from "author name string"
citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
end
end
end
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- use additional properties
for i in pairs(additionalRefProperties) do
for j in pairs(citeParams) do
if not citeMismatch[j] and i18n["cite"][j][i] then
citeParams[j][i18n["cite"][j][i]] = additionalRefProperties[i]
else
citeMismatch[j] = true
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- (1) use the general template for citing web references if there is a match and if at least "reference URL" is present
if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] then
-- we need a processed "stated in" for this template
citeParams['web'][i18n['cite']['web'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, false, self.linked, true)
useCite = citeWeb
useParams = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
useCite = citeQ
useParams = citeParams['q']
end
if useCite and useParams then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(useParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
end
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif not referenceEmpty then
value = "<span style=\"color:#dd3333\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
ref = {value} -- wrap the value object in an array
end
end
return ref
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
okmlxad0hk58sn1cgnll0y9v28zq1o7
803555
803554
2024-08-09T09:49:12Z
en>Janhrach
0
Update from sandbox: clearer algorithm and better variable names in getReference
803555
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18n = require(title .. "/i18n").init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
-- title of general template for citing web references
local citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- title of template that expands stated-in references into citations
local citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2] -- split off namespace from front
-- will hold rendered properties from statement.snaks
local processedProperties = {}
-- will hold properties of the reference which are not directly from statement.snaks,
-- Namely, these are a backup title from "subject named as" and a URL generated from an external ID.
local additionalProcessedProperties = {}
-- for each citation type, there will be an associative array that associates lists of rendered properties
-- to citation-template parameters
local groupedProcessedProperties = {['web'] = {}, ['q'] = {}}
-- like above, but only associates one rendered property to each parameter; if the above variable
-- contains more strings for a parameter, the strings will be assigned to numbered params (e.g. "author1")
local citeParams = {['web'] = {}, ['q'] = {}}
local citeErrors = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local version = 9 -- increment this each time the below logic is changed to avoid conflict errors
if not statement.snaks then
return {}
end
-- don't use bot-added references referencing Wikimedia projects or containing "inferred from" (such references are not usable on Wikipedia)
if statement.snaks[aliasesP.importedFrom] or statement.snaks[aliasesP.wikimediaImportURL] or statement.snaks[aliasesP.inferredFrom] then
return {}
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
local wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
local tempLink = self.conf:getValue(statement.snaks[wikidataPropertyOfSource][1], false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL in square brackets.
-- the link is in wiki markup, so strip the square brackets and the display text
-- gsub also returns another, discarted value, therefore the result is assigned to tempLink first
tempLink = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1")
additionalProcessedProperties[aliasesP.referenceURL] = {tempLink}
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalProcessedProperties[aliasesP.referenceURL]) then
additionalProcessedProperties[aliasesP.title] = {statement.snaks[aliasesP.subjectNamedAs][1].datavalue.value}
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- fill processedProperties
for refProperty in pairs(statement.snaks) do
-- multiple authors may be given
if refProperty == aliasesP.author or refProperty == aliasesP.authorNameString then
processedProperties[refProperty] = self:getReferenceDetails(statement.snaks, refProperty, false, self.linked, true) -- link = true/false, anyLang = true
elseif refProperty == aliasesP.statedIn then
-- Get "stated in" raw, as it is wanted (for Cite Q) even if it doesn't have a local language label.
processedProperties[aliasesP.statedIn] = {self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true)} -- raw = true
else
processedProperties[refProperty] = {self:getReferenceDetail(statement.snaks, refProperty, false, self.linked and (statement.snaks[refProperty][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #processedProperties[refProperty] == 0 then
processedProperties[refProperty] = nil
end
end
-- fill groupedProcessedProperties
for refProperty in pairs(statement.snaks) do
if processedProperties[refProperty] ~= nil then
local i18nKey
local prefix = ""
referenceEmpty = false
if statement.snaks[refProperty][1].datatype == 'external-id' then
i18nKey = "external-id"
prefix = self.conf:getLabel(refProperty)
if prefix ~= "" then
prefix = prefix .. " "
end
else
i18nKey = refProperty
end
-- add the parameter to each matching type of citation
for citeType in pairs(groupedProcessedProperties) do
repeat -- just a simple wrapper to emulate "continue"
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
local paramName = i18n['cite'][citeType][i18nKey]
-- handle unknown properties in the reference
if not paramName then
citeErrors[citeType] = true
break
end
if paramName == "" then
break
end
referenceEmpty = false
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(processedProperties[refProperty]) do
table.insert(groupedProcessedProperties[citeType][paramName], propertyValue)
end
until true
end
end
end
-- handle additional properties
for refProperty in pairs(additionalProcessedProperties) do
for citeType in pairs(citeParams) do
repeat
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
local paramName = i18n['cite'][citeType][refProperty]
-- handle unknown properties in the reference
if not paramName then
citeErrors[citeType] = true
break
end
if paramName == "" then
break
end
referenceEmpty = false
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(additionalProcessedProperties[refProperty]) do
table.insert(groupedProcessedProperties[citeType][paramName], propertyValue)
end
until true
end
end
-- fill citeParams
for citeType in pairs(citeParams) do
for paramName, paramValues in pairs(groupedProcessedProperties[citeType]) do
if #paramValues == 1 then
citeParams[citeType][paramName] = paramValues[1]
else
-- There is more than one value for this parameter - the values will
-- go into separate numbered parameters (e.g. "author1", "author2")
for paramNum, paramValue in pairs(paramValues) do
citeParams[citeType][paramName .. paramNum] = paramValue
end
end
end
end
-- handle missing mandatory parameters for the templates
if not citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] then
citeErrors['web'] = true
end
if not citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
citeErrors['q'] = true
end
local templateToUse = nil
local paramsToUse = nil
-- choose the output template
-- (1) use the general template for citing web references if there is a match and if at least "reference URL" is present
if citeWeb and not citeErrors['web'] then
-- we need a processed "stated in" for this template
citeParams['web'][i18n['cite']['web'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, false, self.linked, true)
templateToUse = citeWeb
paramsToUse = citeParams['web']
-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present
elseif citeQ and not citeErrors['q'] then
templateToUse = citeQ
paramsToUse = citeParams['q']
end
-- fill refContent
local refContent = ""
if templateToUse and paramsToUse then
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(paramsToUse) do
refContent = refContent .. "|" .. i .. "=" .. v
end
refContent = "{{" .. templateToUse .. refContent .. "}}"
else
refContent = mw.getCurrentFrame():expandTemplate{title=templateToUse, args=paramsToUse}
end
-- If the citation couldn't be displayed using Cite web or Cite Q, but is not empty (barring ignored propeties), throw an error
elseif not referenceEmpty then
refContent = "<span style=\"color:#dd3333\">" .. errorText("malformed-reference") .. "</span>"
end
-- wrap refContent
local ref = {}
if refContent ~= "" then
ref = {refContent}
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
ref.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
return {ref}
else
return {}
end
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
g1x7rh8ka7oxh7dyxs8jbfh9hxq1jhw
803556
803555
2024-08-26T09:41:13Z
en>Janhrach
0
update from sandbox -- more configurability
803556
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local arg = ...
local i18n
local i18nPath
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = arg
end
if not i18n then
i18nPath = title .. "/i18n"
i18n = require(i18nPath).init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, param)
local text = i18n["errors"][code]
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
local function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local function isValueInTable(searchedItem, inputTable)
for _, item in pairs(inputTable) do
if item == searchedItem then
return true
end
end
return false
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local citeParamMapping = i18n['cite']['param-mapping']
local citeConfig = i18n['cite']['config']
local citeTypes = i18n['cite']['output-types']
-- will hold rendered properties of the reference which are not directly from statement.snaks,
-- Namely, these are a backup title from "subject named as" and a URL generated from an external ID.
local additionalProcessedProperties = {}
-- for each citation type, there will be an associative array that associates lists of rendered properties
-- to citation-template parameters
local groupedProcessedProperties = {}
-- like above, but only associates one rendered property to each parameter; if the above variable
-- contains more strings for a parameter, the strings will be assigned to numbered params (e.g. "author1")
local citeParams = {}
local citeErrors = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local version = 10 -- increment this each time the below logic is changed to avoid conflict errors
if not statement.snaks then
return {}
end
-- don't use bot-added references referencing Wikimedia projects or containing "inferred from" (such references are not usable on Wikipedia)
if statement.snaks[aliasesP.importedFrom] or statement.snaks[aliasesP.wikimediaImportURL] or statement.snaks[aliasesP.inferredFrom] then
return {}
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
local wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
local tempLink = self:getReferenceDetail(statement.snaks, wikidataPropertyOfSource, false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL in square brackets.
-- the link is in wiki markup, so strip the square brackets and the display text
-- gsub also returns another, discarted value, therefore the result is assigned to tempLink first
tempLink = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1")
additionalProcessedProperties[aliasesP.referenceURL] = {tempLink}
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalProcessedProperties[aliasesP.referenceURL]) then
additionalProcessedProperties[aliasesP.title] = {self:getReferenceDetail(statement.snaks, aliasesP.subjectNamedAs, false, false, true)} -- not raw, not linked, anyLang
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- initialize groupedProcessedProperties and citeParams
for _, citeType in ipairs(citeTypes) do
groupedProcessedProperties[citeType] = {}
citeParams[citeType] = {}
end
-- fill groupedProcessedProperties
for refProperty in pairs(statement.snaks) do
-- add the parameter to each matching type of citation
for _, citeType in ipairs(citeTypes) do
repeat -- just a simple wrapper to emulate "continue"
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
-- set mappingKey and prefix
local mappingKey
local prefix = ""
if statement.snaks[refProperty][1].datatype == 'external-id' then
mappingKey = "external-id"
prefix = self.conf:getLabel(refProperty)
if prefix ~= "" then
prefix = prefix .. " "
end
else
mappingKey = refProperty
end
local paramName = citeParamMapping[citeType][mappingKey]
-- skip properties with empty parameter name
if paramName == "" then
break
end
referenceEmpty = false
-- handle unknown properties in the reference
if not paramName then
citeErrors[citeType] = true
break
end
-- set processedProperty
local processedProperty
local raw = false -- if the value is wanted raw
if isValueInTable(paramName, citeConfig[citeType]["raw-value-params"] or {}) then
raw = true
end
if isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
-- Multiple values may be given.
processedProperty = self:getReferenceDetails(statement.snaks, refProperty, raw, self.linked, true) -- anyLang = true
else
-- If multiple values are given, all but the first one are discarted.
processedProperty = {self:getReferenceDetail(statement.snaks, refProperty, raw, self.linked and (statement.snaks[refProperty][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #processedProperty == 0 then
break
end
-- add an entry to groupedProcessedProperties
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(processedProperty) do
table.insert(groupedProcessedProperties[citeType][paramName], prefix .. propertyValue)
end
until true
end
end
-- handle additional properties
for refProperty in pairs(additionalProcessedProperties) do
for _, citeType in ipairs(citeTypes) do
repeat
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
local paramName = citeParamMapping[citeType][refProperty]
-- handle unknown properties in the reference
if not paramName then
citeErrors[citeType] = true
break
end
if paramName == "" then
break
end
referenceEmpty = false
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(additionalProcessedProperties[refProperty]) do
table.insert(groupedProcessedProperties[citeType][paramName], propertyValue)
end
until true
end
end
-- fill citeParams
for _, citeType in ipairs(citeTypes) do
for paramName, paramValues in pairs(groupedProcessedProperties[citeType]) do
if #paramValues == 1 or not isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
citeParams[citeType][paramName] = paramValues[1]
else
-- There is more than one value for this parameter - the values will
-- go into separate numbered parameters (e.g. "author1", "author2")
for paramNum, paramValue in pairs(paramValues) do
citeParams[citeType][paramName .. paramNum] = paramValue
end
end
end
end
-- handle missing mandatory parameters for the templates
for _, citeType in ipairs(citeTypes) do
for _, requiredCiteParam in pairs(citeConfig[citeType]["mandatory-params"] or {}) do
if not citeParams[citeType][requiredCiteParam] then
citeErrors[citeType] = true
end
end
end
-- handle missing templates in the citeConfig
for _, citeType in ipairs(citeTypes) do
if not citeConfig[citeType]["template"] then
citeErrors[citeType] = true
end
end
local citeTypeToUse = nil
-- choose the output template
for _, citeType in ipairs(citeTypes) do
if not citeErrors[citeType] then
citeTypeToUse = citeType
break
end
end
-- set refContent
local refContent = ""
if citeTypeToUse then
local templateToUse = citeConfig[citeTypeToUse]["template"]
local paramsToUse = citeParams[citeTypeToUse]
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(paramsToUse) do
refContent = refContent .. "|" .. i .. "=" .. v
end
refContent = "{{" .. templateToUse .. refContent .. "}}"
else
xpcall(
function () refContent = mw.getCurrentFrame():expandTemplate{title=templateToUse, args=paramsToUse} end,
function () error(string.format(i18n["errors"]["no-such-reference-template"], templateToUse, i18nPath, citeTypeToUse)) end
)
end
-- If the citation couldn't be displayed using any template, but is not empty (barring ignored propeties), throw an error
elseif not referenceEmpty then
refContent = "<span style=\"color:#dd3333\">" .. errorText("malformed-reference") .. "</span>"
end
-- wrap refContent
local ref = {}
if refContent ~= "" then
ref = {refContent}
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
ref.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
return {ref}
else
return {}
end
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
far7zxbx51awmo4yhoaeqglbj59u7kk
803557
803556
2024-09-02T16:58:49Z
en>Janhrach
0
citation error message improvement and related internal tweaks
803557
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local module_arg = ...
local i18n
local i18nPath
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = module_arg
end
if not i18n then
i18nPath = title .. "/i18n"
i18n = require(i18nPath).init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, ...)
local text = i18n["errors"][code]
if arg then text = mw.ustring.format(text, unpack(arg)) end
return text
end
local function throwError(errorMessage, ...)
error(errorText(errorMessage, unpack(arg)))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local function isValueInTable(searchedItem, inputTable)
for _, item in pairs(inputTable) do
if item == searchedItem then
return true
end
end
return false
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local citeParamMapping = i18n['cite']['param-mapping']
local citeConfig = i18n['cite']['config']
local citeTypes = i18n['cite']['output-types']
-- will hold rendered properties of the reference which are not directly from statement.snaks,
-- Namely, these are a backup title from "subject named as" and a URL generated from an external ID.
local additionalProcessedProperties = {}
-- for each citation type, there will be an associative array that associates lists of rendered properties
-- to citation-template parameters
local groupedProcessedProperties = {}
-- like above, but only associates one rendered property to each parameter; if the above variable
-- contains more strings for a parameter, the strings will be assigned to numbered params (e.g. "author1")
local citeParams = {}
local citeErrors = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local version = 11 -- increment this each time the below logic is changed to avoid conflict errors
if not statement.snaks then
return {}
end
-- don't use bot-added references referencing Wikimedia projects or containing "inferred from" (such references are not usable on Wikipedia)
if statement.snaks[aliasesP.importedFrom] or statement.snaks[aliasesP.wikimediaImportURL] or statement.snaks[aliasesP.inferredFrom] then
return {}
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
local wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
local tempLink = self:getReferenceDetail(statement.snaks, wikidataPropertyOfSource, false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL in square brackets.
-- the link is in wiki markup, so strip the square brackets and the display text
-- gsub also returns another, discarted value, therefore the result is assigned to tempLink first
tempLink = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1")
additionalProcessedProperties[aliasesP.referenceURL] = {tempLink}
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalProcessedProperties[aliasesP.referenceURL]) then
additionalProcessedProperties[aliasesP.title] = {self:getReferenceDetail(statement.snaks, aliasesP.subjectNamedAs, false, false, true)} -- not raw, not linked, anyLang
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- initialize groupedProcessedProperties and citeParams
for _, citeType in ipairs(citeTypes) do
groupedProcessedProperties[citeType] = {}
citeParams[citeType] = {}
end
-- fill groupedProcessedProperties
for refProperty in pairs(statement.snaks) do
-- add the parameter to each matching type of citation
for _, citeType in ipairs(citeTypes) do
repeat -- just a simple wrapper to emulate "continue"
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
-- set mappingKey and prefix
local mappingKey
local prefix = ""
if statement.snaks[refProperty][1].datatype == 'external-id' then
mappingKey = "external-id"
prefix = self.conf:getLabel(refProperty)
if prefix ~= "" then
prefix = prefix .. " "
end
else
mappingKey = refProperty
end
local paramName = citeParamMapping[citeType][mappingKey]
-- skip properties with empty parameter name
if paramName == "" then
break
end
referenceEmpty = false
-- handle unknown properties in the reference
if not paramName then
local error_message = errorText("unknown-property-in-ref", refProperty)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
break
end
-- set processedProperty
local processedProperty
local raw = false -- if the value is wanted raw
if isValueInTable(paramName, citeConfig[citeType]["raw-value-params"] or {}) then
raw = true
end
if isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
-- Multiple values may be given.
processedProperty = self:getReferenceDetails(statement.snaks, refProperty, raw, self.linked, true) -- anyLang = true
else
-- If multiple values are given, all but the first suitable one are discarted.
processedProperty = {self:getReferenceDetail(statement.snaks, refProperty, raw, self.linked and (statement.snaks[refProperty][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #processedProperty == 0 then
break
end
-- add an entry to groupedProcessedProperties
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(processedProperty) do
table.insert(groupedProcessedProperties[citeType][paramName], prefix .. propertyValue)
end
until true
end
end
-- handle additional properties
for refProperty in pairs(additionalProcessedProperties) do
for _, citeType in ipairs(citeTypes) do
repeat
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
local paramName = citeParamMapping[citeType][refProperty]
-- handle unknown properties in the reference
if not paramName then
-- Skip this additional property, but do not cause an error.
break
end
if paramName == "" then
break
end
referenceEmpty = false
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(additionalProcessedProperties[refProperty]) do
table.insert(groupedProcessedProperties[citeType][paramName], propertyValue)
end
until true
end
end
-- fill citeParams
for _, citeType in ipairs(citeTypes) do
for paramName, paramValues in pairs(groupedProcessedProperties[citeType]) do
if #paramValues == 1 or not isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
citeParams[citeType][paramName] = paramValues[1]
else
-- There is more than one value for this parameter - the values will
-- go into separate numbered parameters (e.g. "author1", "author2")
for paramNum, paramValue in pairs(paramValues) do
citeParams[citeType][paramName .. paramNum] = paramValue
end
end
end
end
-- handle missing mandatory parameters for the templates
for _, citeType in ipairs(citeTypes) do
for _, requiredCiteParam in pairs(citeConfig[citeType]["mandatory-params"] or {}) do
if not citeParams[citeType][requiredCiteParam] then -- The required param is not present.
if citeErrors[citeType] then -- Do not override the previous error, if it exists.
break
end
local error_message = errorText("missing-mandatory-param", requiredCiteParam)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
end
end
end
local citeTypeToUse = nil
-- choose the output template
for _, citeType in ipairs(citeTypes) do
if not citeErrors[citeType] then
citeTypeToUse = citeType
break
end
end
-- set refContent
local refContent = ""
if citeTypeToUse then
local templateToUse = citeConfig[citeTypeToUse]["template"]
local paramsToUse = citeParams[citeTypeToUse]
if not templateToUse or templateToUse == "" then
throwError("no-such-reference-template", tostring(templateToUse), i18nPath, citeTypeToUse)
end
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(paramsToUse) do
refContent = refContent .. "|" .. i .. "=" .. v
end
refContent = "{{" .. templateToUse .. refContent .. "}}"
else
xpcall(
function () refContent = mw.getCurrentFrame():expandTemplate{title=templateToUse, args=paramsToUse} end,
function () throwError("no-such-reference-template", templateToUse, i18nPath, citeTypeToUse) end
)
end
-- If the citation couldn't be displayed using any template, but is not empty (barring ignored propeties), throw an error.
elseif not referenceEmpty then
refContent = errorText("malformed-reference-header")
for _, citeType in ipairs(citeTypes) do
refContent = refContent .. errorText("template-failure-reason", citeConfig[citeType]["template"], citeErrors[citeType])
end
refContent = refContent .. errorText("malformed-reference-footer")
end
-- wrap refContent
local ref = {}
if refContent ~= "" then
ref = {refContent}
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
ref.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['version']) + version)
end
return {ref}
else
return {}
end
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
7tb3f3tkj144piju76l6sugrzm31r8m
803558
803557
2024-09-12T18:29:19Z
en>Janhrach
0
Update from sandbox – use proper fallback for labels
803558
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local module_arg = ...
local i18n
local i18nPath
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = module_arg
end
if not i18n then
i18nPath = title .. "/i18n"
i18n = require(i18nPath).init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, ...)
local text = i18n["errors"][code]
if arg then text = mw.ustring.format(text, unpack(arg)) end
return text
end
local function throwError(errorMessage, ...)
error(errorText(errorMessage, unpack(arg)))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local function isValueInTable(searchedItem, inputTable)
for _, item in pairs(inputTable) do
if item == searchedItem then
return true
end
end
return false
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabel(id)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local citeParamMapping = i18n['cite']['param-mapping']
local citeConfig = i18n['cite']['config']
local citeTypes = i18n['cite']['output-types']
-- will hold rendered properties of the reference which are not directly from statement.snaks,
-- Namely, these are a backup title from "subject named as" and a URL generated from an external ID.
local additionalProcessedProperties = {}
-- for each citation type, there will be an associative array that associates lists of rendered properties
-- to citation-template parameters
local groupedProcessedProperties = {}
-- like above, but only associates one rendered property to each parameter; if the above variable
-- contains more strings for a parameter, the strings will be assigned to numbered params (e.g. "author1")
local citeParams = {}
local citeErrors = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local version = 11 -- increment this each time the below logic is changed to avoid conflict errors
if not statement.snaks then
return {}
end
-- don't use bot-added references referencing Wikimedia projects or containing "inferred from" (such references are not usable on Wikipedia)
if statement.snaks[aliasesP.importedFrom] or statement.snaks[aliasesP.wikimediaImportURL] or statement.snaks[aliasesP.inferredFrom] then
return {}
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
local wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
local tempLink = self:getReferenceDetail(statement.snaks, wikidataPropertyOfSource, false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL in square brackets.
-- the link is in wiki markup, so strip the square brackets and the display text
-- gsub also returns another, discarted value, therefore the result is assigned to tempLink first
tempLink = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1")
additionalProcessedProperties[aliasesP.referenceURL] = {tempLink}
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalProcessedProperties[aliasesP.referenceURL]) then
additionalProcessedProperties[aliasesP.title] = {self:getReferenceDetail(statement.snaks, aliasesP.subjectNamedAs, false, false, true)} -- not raw, not linked, anyLang
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- initialize groupedProcessedProperties and citeParams
for _, citeType in ipairs(citeTypes) do
groupedProcessedProperties[citeType] = {}
citeParams[citeType] = {}
end
-- fill groupedProcessedProperties
for refProperty in pairs(statement.snaks) do
-- add the parameter to each matching type of citation
for _, citeType in ipairs(citeTypes) do
repeat -- just a simple wrapper to emulate "continue"
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
-- set mappingKey and prefix
local mappingKey
local prefix = ""
if statement.snaks[refProperty][1].datatype == 'external-id' then
mappingKey = "external-id"
prefix = self.conf:getLabel(refProperty)
if prefix ~= "" then
prefix = prefix .. " "
end
else
mappingKey = refProperty
end
local paramName = citeParamMapping[citeType][mappingKey]
-- skip properties with empty parameter name
if paramName == "" then
break
end
referenceEmpty = false
-- handle unknown properties in the reference
if not paramName then
local error_message = errorText("unknown-property-in-ref", refProperty)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
break
end
-- set processedProperty
local processedProperty
local raw = false -- if the value is wanted raw
if isValueInTable(paramName, citeConfig[citeType]["raw-value-params"] or {}) then
raw = true
end
if isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
-- Multiple values may be given.
processedProperty = self:getReferenceDetails(statement.snaks, refProperty, raw, self.linked, true) -- anyLang = true
else
-- If multiple values are given, all but the first suitable one are discarted.
processedProperty = {self:getReferenceDetail(statement.snaks, refProperty, raw, self.linked and (statement.snaks[refProperty][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #processedProperty == 0 then
break
end
-- add an entry to groupedProcessedProperties
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(processedProperty) do
table.insert(groupedProcessedProperties[citeType][paramName], prefix .. propertyValue)
end
until true
end
end
-- handle additional properties
for refProperty in pairs(additionalProcessedProperties) do
for _, citeType in ipairs(citeTypes) do
repeat
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
local paramName = citeParamMapping[citeType][refProperty]
-- handle unknown properties in the reference
if not paramName then
-- Skip this additional property, but do not cause an error.
break
end
if paramName == "" then
break
end
referenceEmpty = false
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(additionalProcessedProperties[refProperty]) do
table.insert(groupedProcessedProperties[citeType][paramName], propertyValue)
end
until true
end
end
-- fill citeParams
for _, citeType in ipairs(citeTypes) do
for paramName, paramValues in pairs(groupedProcessedProperties[citeType]) do
if #paramValues == 1 or not isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
citeParams[citeType][paramName] = paramValues[1]
else
-- There is more than one value for this parameter - the values will
-- go into separate numbered parameters (e.g. "author1", "author2")
for paramNum, paramValue in pairs(paramValues) do
citeParams[citeType][paramName .. paramNum] = paramValue
end
end
end
end
-- handle missing mandatory parameters for the templates
for _, citeType in ipairs(citeTypes) do
for _, requiredCiteParam in pairs(citeConfig[citeType]["mandatory-params"] or {}) do
if not citeParams[citeType][requiredCiteParam] then -- The required param is not present.
if citeErrors[citeType] then -- Do not override the previous error, if it exists.
break
end
local error_message = errorText("missing-mandatory-param", requiredCiteParam)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
end
end
end
local citeTypeToUse = nil
-- choose the output template
for _, citeType in ipairs(citeTypes) do
if not citeErrors[citeType] then
citeTypeToUse = citeType
break
end
end
-- set refContent
local refContent = ""
if citeTypeToUse then
local templateToUse = citeConfig[citeTypeToUse]["template"]
local paramsToUse = citeParams[citeTypeToUse]
if not templateToUse or templateToUse == "" then
throwError("no-such-reference-template", tostring(templateToUse), i18nPath, citeTypeToUse)
end
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(paramsToUse) do
refContent = refContent .. "|" .. i .. "=" .. v
end
refContent = "{{" .. templateToUse .. refContent .. "}}"
else
xpcall(
function () refContent = mw.getCurrentFrame():expandTemplate{title=templateToUse, args=paramsToUse} end,
function () throwError("no-such-reference-template", templateToUse, i18nPath, citeTypeToUse) end
)
end
-- If the citation couldn't be displayed using any template, but is not empty (barring ignored propeties), throw an error.
elseif not referenceEmpty then
refContent = errorText("malformed-reference-header")
for _, citeType in ipairs(citeTypes) do
refContent = refContent .. errorText("template-failure-reason", citeConfig[citeType]["template"], citeErrors[citeType])
end
refContent = refContent .. errorText("malformed-reference-footer")
end
-- wrap refContent
local ref = {}
if refContent ~= "" then
ref = {refContent}
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
ref.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['version']) + version)
end
return {ref}
else
return {}
end
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
klnosw6qk6m6vbnazowivije405gt0c
803559
803558
2024-10-05T13:15:17Z
en>Janhrach
0
update – add publication place to aliasesQ
803559
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local module_arg = ...
local i18n
local i18nPath
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = module_arg
end
if not i18n then
i18nPath = title .. "/i18n"
i18n = require(i18nPath).init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433",
placeOfPublication = "P291"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, ...)
local text = i18n["errors"][code]
if arg then text = mw.ustring.format(text, unpack(arg)) end
return text
end
local function throwError(errorMessage, ...)
error(errorText(errorMessage, unpack(arg)))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local function isValueInTable(searchedItem, inputTable)
for _, item in pairs(inputTable) do
if item == searchedItem then
return true
end
end
return false
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabel(id)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local citeParamMapping = i18n['cite']['param-mapping']
local citeConfig = i18n['cite']['config']
local citeTypes = i18n['cite']['output-types']
-- will hold rendered properties of the reference which are not directly from statement.snaks,
-- Namely, these are a backup title from "subject named as" and a URL generated from an external ID.
local additionalProcessedProperties = {}
-- for each citation type, there will be an associative array that associates lists of rendered properties
-- to citation-template parameters
local groupedProcessedProperties = {}
-- like above, but only associates one rendered property to each parameter; if the above variable
-- contains more strings for a parameter, the strings will be assigned to numbered params (e.g. "author1")
local citeParams = {}
local citeErrors = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local version = 11 -- increment this each time the below logic is changed to avoid conflict errors
if not statement.snaks then
return {}
end
-- don't use bot-added references referencing Wikimedia projects or containing "inferred from" (such references are not usable on Wikipedia)
if statement.snaks[aliasesP.importedFrom] or statement.snaks[aliasesP.wikimediaImportURL] or statement.snaks[aliasesP.inferredFrom] then
return {}
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
local wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
local tempLink = self:getReferenceDetail(statement.snaks, wikidataPropertyOfSource, false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL in square brackets.
-- the link is in wiki markup, so strip the square brackets and the display text
-- gsub also returns another, discarted value, therefore the result is assigned to tempLink first
tempLink = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1")
additionalProcessedProperties[aliasesP.referenceURL] = {tempLink}
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalProcessedProperties[aliasesP.referenceURL]) then
additionalProcessedProperties[aliasesP.title] = {self:getReferenceDetail(statement.snaks, aliasesP.subjectNamedAs, false, false, true)} -- not raw, not linked, anyLang
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- initialize groupedProcessedProperties and citeParams
for _, citeType in ipairs(citeTypes) do
groupedProcessedProperties[citeType] = {}
citeParams[citeType] = {}
end
-- fill groupedProcessedProperties
for refProperty in pairs(statement.snaks) do
-- add the parameter to each matching type of citation
for _, citeType in ipairs(citeTypes) do
repeat -- just a simple wrapper to emulate "continue"
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
-- set mappingKey and prefix
local mappingKey
local prefix = ""
if statement.snaks[refProperty][1].datatype == 'external-id' then
mappingKey = "external-id"
prefix = self.conf:getLabel(refProperty)
if prefix ~= "" then
prefix = prefix .. " "
end
else
mappingKey = refProperty
end
local paramName = citeParamMapping[citeType][mappingKey]
-- skip properties with empty parameter name
if paramName == "" then
break
end
referenceEmpty = false
-- handle unknown properties in the reference
if not paramName then
local error_message = errorText("unknown-property-in-ref", refProperty)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
break
end
-- set processedProperty
local processedProperty
local raw = false -- if the value is wanted raw
if isValueInTable(paramName, citeConfig[citeType]["raw-value-params"] or {}) then
raw = true
end
if isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
-- Multiple values may be given.
processedProperty = self:getReferenceDetails(statement.snaks, refProperty, raw, self.linked, true) -- anyLang = true
else
-- If multiple values are given, all but the first suitable one are discarted.
processedProperty = {self:getReferenceDetail(statement.snaks, refProperty, raw, self.linked and (statement.snaks[refProperty][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #processedProperty == 0 then
break
end
-- add an entry to groupedProcessedProperties
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(processedProperty) do
table.insert(groupedProcessedProperties[citeType][paramName], prefix .. propertyValue)
end
until true
end
end
-- handle additional properties
for refProperty in pairs(additionalProcessedProperties) do
for _, citeType in ipairs(citeTypes) do
repeat
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
local paramName = citeParamMapping[citeType][refProperty]
-- handle unknown properties in the reference
if not paramName then
-- Skip this additional property, but do not cause an error.
break
end
if paramName == "" then
break
end
referenceEmpty = false
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(additionalProcessedProperties[refProperty]) do
table.insert(groupedProcessedProperties[citeType][paramName], propertyValue)
end
until true
end
end
-- fill citeParams
for _, citeType in ipairs(citeTypes) do
for paramName, paramValues in pairs(groupedProcessedProperties[citeType]) do
if #paramValues == 1 or not isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
citeParams[citeType][paramName] = paramValues[1]
else
-- There is more than one value for this parameter - the values will
-- go into separate numbered parameters (e.g. "author1", "author2")
for paramNum, paramValue in pairs(paramValues) do
citeParams[citeType][paramName .. paramNum] = paramValue
end
end
end
end
-- handle missing mandatory parameters for the templates
for _, citeType in ipairs(citeTypes) do
for _, requiredCiteParam in pairs(citeConfig[citeType]["mandatory-params"] or {}) do
if not citeParams[citeType][requiredCiteParam] then -- The required param is not present.
if citeErrors[citeType] then -- Do not override the previous error, if it exists.
break
end
local error_message = errorText("missing-mandatory-param", requiredCiteParam)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
end
end
end
local citeTypeToUse = nil
-- choose the output template
for _, citeType in ipairs(citeTypes) do
if not citeErrors[citeType] then
citeTypeToUse = citeType
break
end
end
-- set refContent
local refContent = ""
if citeTypeToUse then
local templateToUse = citeConfig[citeTypeToUse]["template"]
local paramsToUse = citeParams[citeTypeToUse]
if not templateToUse or templateToUse == "" then
throwError("no-such-reference-template", tostring(templateToUse), i18nPath, citeTypeToUse)
end
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(paramsToUse) do
refContent = refContent .. "|" .. i .. "=" .. v
end
refContent = "{{" .. templateToUse .. refContent .. "}}"
else
xpcall(
function () refContent = mw.getCurrentFrame():expandTemplate{title=templateToUse, args=paramsToUse} end,
function () throwError("no-such-reference-template", templateToUse, i18nPath, citeTypeToUse) end
)
end
-- If the citation couldn't be displayed using any template, but is not empty (barring ignored propeties), throw an error.
elseif not referenceEmpty then
refContent = errorText("malformed-reference-header")
for _, citeType in ipairs(citeTypes) do
refContent = refContent .. errorText("template-failure-reason", citeConfig[citeType]["template"], citeErrors[citeType])
end
refContent = refContent .. errorText("malformed-reference-footer")
end
-- wrap refContent
local ref = {}
if refContent ~= "" then
ref = {refContent}
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
ref.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['version']) + version)
end
return {ref}
else
return {}
end
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
dfwb49viphxxyyv065y7kt387kgposz
803560
803559
2024-10-05T14:22:38Z
en>Janhrach
0
Undid revision [[Special:Diff/1249540315|1249540315]] by [[Special:Contributions/Janhrach|Janhrach]] ([[User talk:Janhrach|talk]])
803560
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local module_arg = ...
local i18n
local i18nPath
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = module_arg
end
if not i18n then
i18nPath = title .. "/i18n"
i18n = require(i18nPath).init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, ...)
local text = i18n["errors"][code]
if arg then text = mw.ustring.format(text, unpack(arg)) end
return text
end
local function throwError(errorMessage, ...)
error(errorText(errorMessage, unpack(arg)))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local function isValueInTable(searchedItem, inputTable)
for _, item in pairs(inputTable) do
if item == searchedItem then
return true
end
end
return false
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabel(id)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local citeParamMapping = i18n['cite']['param-mapping']
local citeConfig = i18n['cite']['config']
local citeTypes = i18n['cite']['output-types']
-- will hold rendered properties of the reference which are not directly from statement.snaks,
-- Namely, these are a backup title from "subject named as" and a URL generated from an external ID.
local additionalProcessedProperties = {}
-- for each citation type, there will be an associative array that associates lists of rendered properties
-- to citation-template parameters
local groupedProcessedProperties = {}
-- like above, but only associates one rendered property to each parameter; if the above variable
-- contains more strings for a parameter, the strings will be assigned to numbered params (e.g. "author1")
local citeParams = {}
local citeErrors = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local version = 11 -- increment this each time the below logic is changed to avoid conflict errors
if not statement.snaks then
return {}
end
-- don't use bot-added references referencing Wikimedia projects or containing "inferred from" (such references are not usable on Wikipedia)
if statement.snaks[aliasesP.importedFrom] or statement.snaks[aliasesP.wikimediaImportURL] or statement.snaks[aliasesP.inferredFrom] then
return {}
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
local wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
local tempLink = self:getReferenceDetail(statement.snaks, wikidataPropertyOfSource, false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL in square brackets.
-- the link is in wiki markup, so strip the square brackets and the display text
-- gsub also returns another, discarted value, therefore the result is assigned to tempLink first
tempLink = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1")
additionalProcessedProperties[aliasesP.referenceURL] = {tempLink}
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalProcessedProperties[aliasesP.referenceURL]) then
additionalProcessedProperties[aliasesP.title] = {self:getReferenceDetail(statement.snaks, aliasesP.subjectNamedAs, false, false, true)} -- not raw, not linked, anyLang
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- initialize groupedProcessedProperties and citeParams
for _, citeType in ipairs(citeTypes) do
groupedProcessedProperties[citeType] = {}
citeParams[citeType] = {}
end
-- fill groupedProcessedProperties
for refProperty in pairs(statement.snaks) do
-- add the parameter to each matching type of citation
for _, citeType in ipairs(citeTypes) do
repeat -- just a simple wrapper to emulate "continue"
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
-- set mappingKey and prefix
local mappingKey
local prefix = ""
if statement.snaks[refProperty][1].datatype == 'external-id' then
mappingKey = "external-id"
prefix = self.conf:getLabel(refProperty)
if prefix ~= "" then
prefix = prefix .. " "
end
else
mappingKey = refProperty
end
local paramName = citeParamMapping[citeType][mappingKey]
-- skip properties with empty parameter name
if paramName == "" then
break
end
referenceEmpty = false
-- handle unknown properties in the reference
if not paramName then
local error_message = errorText("unknown-property-in-ref", refProperty)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
break
end
-- set processedProperty
local processedProperty
local raw = false -- if the value is wanted raw
if isValueInTable(paramName, citeConfig[citeType]["raw-value-params"] or {}) then
raw = true
end
if isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
-- Multiple values may be given.
processedProperty = self:getReferenceDetails(statement.snaks, refProperty, raw, self.linked, true) -- anyLang = true
else
-- If multiple values are given, all but the first suitable one are discarted.
processedProperty = {self:getReferenceDetail(statement.snaks, refProperty, raw, self.linked and (statement.snaks[refProperty][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #processedProperty == 0 then
break
end
-- add an entry to groupedProcessedProperties
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(processedProperty) do
table.insert(groupedProcessedProperties[citeType][paramName], prefix .. propertyValue)
end
until true
end
end
-- handle additional properties
for refProperty in pairs(additionalProcessedProperties) do
for _, citeType in ipairs(citeTypes) do
repeat
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
local paramName = citeParamMapping[citeType][refProperty]
-- handle unknown properties in the reference
if not paramName then
-- Skip this additional property, but do not cause an error.
break
end
if paramName == "" then
break
end
referenceEmpty = false
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(additionalProcessedProperties[refProperty]) do
table.insert(groupedProcessedProperties[citeType][paramName], propertyValue)
end
until true
end
end
-- fill citeParams
for _, citeType in ipairs(citeTypes) do
for paramName, paramValues in pairs(groupedProcessedProperties[citeType]) do
if #paramValues == 1 or not isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
citeParams[citeType][paramName] = paramValues[1]
else
-- There is more than one value for this parameter - the values will
-- go into separate numbered parameters (e.g. "author1", "author2")
for paramNum, paramValue in pairs(paramValues) do
citeParams[citeType][paramName .. paramNum] = paramValue
end
end
end
end
-- handle missing mandatory parameters for the templates
for _, citeType in ipairs(citeTypes) do
for _, requiredCiteParam in pairs(citeConfig[citeType]["mandatory-params"] or {}) do
if not citeParams[citeType][requiredCiteParam] then -- The required param is not present.
if citeErrors[citeType] then -- Do not override the previous error, if it exists.
break
end
local error_message = errorText("missing-mandatory-param", requiredCiteParam)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
end
end
end
local citeTypeToUse = nil
-- choose the output template
for _, citeType in ipairs(citeTypes) do
if not citeErrors[citeType] then
citeTypeToUse = citeType
break
end
end
-- set refContent
local refContent = ""
if citeTypeToUse then
local templateToUse = citeConfig[citeTypeToUse]["template"]
local paramsToUse = citeParams[citeTypeToUse]
if not templateToUse or templateToUse == "" then
throwError("no-such-reference-template", tostring(templateToUse), i18nPath, citeTypeToUse)
end
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(paramsToUse) do
refContent = refContent .. "|" .. i .. "=" .. v
end
refContent = "{{" .. templateToUse .. refContent .. "}}"
else
xpcall(
function () refContent = mw.getCurrentFrame():expandTemplate{title=templateToUse, args=paramsToUse} end,
function () throwError("no-such-reference-template", templateToUse, i18nPath, citeTypeToUse) end
)
end
-- If the citation couldn't be displayed using any template, but is not empty (barring ignored propeties), throw an error.
elseif not referenceEmpty then
refContent = errorText("malformed-reference-header")
for _, citeType in ipairs(citeTypes) do
refContent = refContent .. errorText("template-failure-reason", citeConfig[citeType]["template"], citeErrors[citeType])
end
refContent = refContent .. errorText("malformed-reference-footer")
end
-- wrap refContent
local ref = {}
if refContent ~= "" then
ref = {refContent}
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
ref.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['version']) + version)
end
return {ref}
else
return {}
end
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
local valuesArray, refHash
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
klnosw6qk6m6vbnazowivije405gt0c
803561
803560
2024-11-20T21:00:06Z
en>Uzume
0
remove unused local "refHash" and narrow lifetime/scope of "valuesArray"
803561
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local module_arg = ...
local i18n
local i18nPath
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = module_arg
end
if not i18n then
i18nPath = title .. "/i18n"
i18n = require(i18nPath).init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, ...)
local text = i18n["errors"][code]
if arg then text = mw.ustring.format(text, unpack(arg)) end
return text
end
local function throwError(errorMessage, ...)
error(errorText(errorMessage, unpack(arg)))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local function isValueInTable(searchedItem, inputTable)
for _, item in pairs(inputTable) do
if item == searchedItem then
return true
end
end
return false
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabel(id)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local citeParamMapping = i18n['cite']['param-mapping']
local citeConfig = i18n['cite']['config']
local citeTypes = i18n['cite']['output-types']
-- will hold rendered properties of the reference which are not directly from statement.snaks,
-- Namely, these are a backup title from "subject named as" and a URL generated from an external ID.
local additionalProcessedProperties = {}
-- for each citation type, there will be an associative array that associates lists of rendered properties
-- to citation-template parameters
local groupedProcessedProperties = {}
-- like above, but only associates one rendered property to each parameter; if the above variable
-- contains more strings for a parameter, the strings will be assigned to numbered params (e.g. "author1")
local citeParams = {}
local citeErrors = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local version = 11 -- increment this each time the below logic is changed to avoid conflict errors
if not statement.snaks then
return {}
end
-- don't use bot-added references referencing Wikimedia projects or containing "inferred from" (such references are not usable on Wikipedia)
if statement.snaks[aliasesP.importedFrom] or statement.snaks[aliasesP.wikimediaImportURL] or statement.snaks[aliasesP.inferredFrom] then
return {}
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
local wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
local tempLink = self:getReferenceDetail(statement.snaks, wikidataPropertyOfSource, false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL in square brackets.
-- the link is in wiki markup, so strip the square brackets and the display text
-- gsub also returns another, discarted value, therefore the result is assigned to tempLink first
tempLink = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1")
additionalProcessedProperties[aliasesP.referenceURL] = {tempLink}
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- don't include "subject named as", but use it as the title when "title" is not present but a URL is
if statement.snaks[aliasesP.subjectNamedAs] then
if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalProcessedProperties[aliasesP.referenceURL]) then
additionalProcessedProperties[aliasesP.title] = {self:getReferenceDetail(statement.snaks, aliasesP.subjectNamedAs, false, false, true)} -- not raw, not linked, anyLang
end
statement.snaks[aliasesP.subjectNamedAs] = nil
end
-- initialize groupedProcessedProperties and citeParams
for _, citeType in ipairs(citeTypes) do
groupedProcessedProperties[citeType] = {}
citeParams[citeType] = {}
end
-- fill groupedProcessedProperties
for refProperty in pairs(statement.snaks) do
-- add the parameter to each matching type of citation
for _, citeType in ipairs(citeTypes) do
repeat -- just a simple wrapper to emulate "continue"
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
-- set mappingKey and prefix
local mappingKey
local prefix = ""
if statement.snaks[refProperty][1].datatype == 'external-id' then
mappingKey = "external-id"
prefix = self.conf:getLabel(refProperty)
if prefix ~= "" then
prefix = prefix .. " "
end
else
mappingKey = refProperty
end
local paramName = citeParamMapping[citeType][mappingKey]
-- skip properties with empty parameter name
if paramName == "" then
break
end
referenceEmpty = false
-- handle unknown properties in the reference
if not paramName then
local error_message = errorText("unknown-property-in-ref", refProperty)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
break
end
-- set processedProperty
local processedProperty
local raw = false -- if the value is wanted raw
if isValueInTable(paramName, citeConfig[citeType]["raw-value-params"] or {}) then
raw = true
end
if isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
-- Multiple values may be given.
processedProperty = self:getReferenceDetails(statement.snaks, refProperty, raw, self.linked, true) -- anyLang = true
else
-- If multiple values are given, all but the first suitable one are discarted.
processedProperty = {self:getReferenceDetail(statement.snaks, refProperty, raw, self.linked and (statement.snaks[refProperty][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #processedProperty == 0 then
break
end
-- add an entry to groupedProcessedProperties
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(processedProperty) do
table.insert(groupedProcessedProperties[citeType][paramName], prefix .. propertyValue)
end
until true
end
end
-- handle additional properties
for refProperty in pairs(additionalProcessedProperties) do
for _, citeType in ipairs(citeTypes) do
repeat
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
local paramName = citeParamMapping[citeType][refProperty]
-- handle unknown properties in the reference
if not paramName then
-- Skip this additional property, but do not cause an error.
break
end
if paramName == "" then
break
end
referenceEmpty = false
if not groupedProcessedProperties[citeType][paramName] then
groupedProcessedProperties[citeType][paramName] = {}
end
for _, propertyValue in pairs(additionalProcessedProperties[refProperty]) do
table.insert(groupedProcessedProperties[citeType][paramName], propertyValue)
end
until true
end
end
-- fill citeParams
for _, citeType in ipairs(citeTypes) do
for paramName, paramValues in pairs(groupedProcessedProperties[citeType]) do
if #paramValues == 1 or not isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
citeParams[citeType][paramName] = paramValues[1]
else
-- There is more than one value for this parameter - the values will
-- go into separate numbered parameters (e.g. "author1", "author2")
for paramNum, paramValue in pairs(paramValues) do
citeParams[citeType][paramName .. paramNum] = paramValue
end
end
end
end
-- handle missing mandatory parameters for the templates
for _, citeType in ipairs(citeTypes) do
for _, requiredCiteParam in pairs(citeConfig[citeType]["mandatory-params"] or {}) do
if not citeParams[citeType][requiredCiteParam] then -- The required param is not present.
if citeErrors[citeType] then -- Do not override the previous error, if it exists.
break
end
local error_message = errorText("missing-mandatory-param", requiredCiteParam)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
end
end
end
local citeTypeToUse = nil
-- choose the output template
for _, citeType in ipairs(citeTypes) do
if not citeErrors[citeType] then
citeTypeToUse = citeType
break
end
end
-- set refContent
local refContent = ""
if citeTypeToUse then
local templateToUse = citeConfig[citeTypeToUse]["template"]
local paramsToUse = citeParams[citeTypeToUse]
if not templateToUse or templateToUse == "" then
throwError("no-such-reference-template", tostring(templateToUse), i18nPath, citeTypeToUse)
end
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(paramsToUse) do
refContent = refContent .. "|" .. i .. "=" .. v
end
refContent = "{{" .. templateToUse .. refContent .. "}}"
else
xpcall(
function () refContent = mw.getCurrentFrame():expandTemplate{title=templateToUse, args=paramsToUse} end,
function () throwError("no-such-reference-template", templateToUse, i18nPath, citeTypeToUse) end
)
end
-- If the citation couldn't be displayed using any template, but is not empty (barring ignored propeties), throw an error.
elseif not referenceEmpty then
refContent = errorText("malformed-reference-header")
for _, citeType in ipairs(citeTypes) do
refContent = refContent .. errorText("template-failure-reason", citeConfig[citeType]["template"], citeErrors[citeType])
end
refContent = refContent .. errorText("malformed-reference-footer")
end
-- wrap refContent
local ref = {}
if refContent ~= "" then
ref = {refContent}
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
ref.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['version']) + version)
end
return {ref}
else
return {}
end
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
local valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
fw0wujzhw9loowczj4s2zaqoeawla5r
803562
803561
2025-07-22T19:53:47Z
en>Janhrach
0
update from sandbox
803562
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local module_arg = ...
local i18n
local i18nPath
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = module_arg
end
if not i18n then
i18nPath = title .. "/i18n"
i18n = require(i18nPath).init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433",
lastUpdate = "P5017"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, ...)
local text = i18n["errors"][code]
if arg then text = mw.ustring.format(text, unpack(arg)) end
return text
end
local function throwError(errorMessage, ...)
error(errorText(errorMessage, unpack(arg)))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local function isValueInTable(searchedItem, inputTable)
for _, item in pairs(inputTable) do
if item == searchedItem then
return true
end
end
return false
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabel(id)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local citeParamMapping = i18n['cite']['param-mapping']
local citeConfig = i18n['cite']['config']
local citeTypes = i18n['cite']['output-types']
-- will hold rendered properties of the reference which are not directly from statement.snaks,
-- Namely, is URL generated from an external ID.
local additionalProcessedProperties = {}
-- for each citation type, there will be an associative array that associates lists of rendered properties
-- to citation-template parameters
local candidateParams = {}
-- like above, but only associates one rendered property to each parameter; if the above variable
-- contains more strings for a parameter, the strings will be assigned to numbered params (e.g. "author1")
local citeParams = {}
local citeErrors = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local version = 12 -- increment this each time the below logic is changed to avoid conflict errors
if not statement.snaks then
return {}
end
-- don't use bot-added references referencing Wikimedia projects or containing "inferred from" (such references are not usable on Wikipedia)
if statement.snaks[aliasesP.importedFrom] or statement.snaks[aliasesP.wikimediaImportURL] or statement.snaks[aliasesP.inferredFrom] then
return {}
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
local wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
local tempLink = self:getReferenceDetail(statement.snaks, wikidataPropertyOfSource, false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL in square brackets.
-- the link is in wiki markup, so strip the square brackets and the display text
-- gsub also returns another, discarted value, therefore the result is assigned to tempLink first
tempLink = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1")
additionalProcessedProperties[aliasesP.referenceURL] = {tempLink}
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- initialize candidateParams and citeParams
for _, citeType in ipairs(citeTypes) do
candidateParams[citeType] = {}
citeParams[citeType] = {}
end
-- fill candidateParams
for _, citeType in ipairs(citeTypes) do
-- This will contain value--priority pairs for each param name.
local candidateValuesAndPriorities = {}
-- fill candidateValuesAndPriorities
for refProperty in pairs(statement.snaks) do
if citeErrors[citeType] then
break
end
repeat -- just a simple wrapper to emulate "continue"
-- set mappingKey and prefix
local mappingKey
local prefix = ""
if statement.snaks[refProperty][1].datatype == 'external-id' then
mappingKey = "external-id"
prefix = self.conf:getLabel(refProperty)
if prefix ~= "" then
prefix = prefix .. " "
end
else
mappingKey = refProperty
end
local paramName = citeParamMapping[citeType][mappingKey]
-- skip properties with empty parameter name
if paramName == "" then
break -- skip this property for this value of citeType
end
-- handle unknown properties in the reference
if not paramName then
referenceEmpty = false
local error_message = errorText("unknown-property-in-ref", refProperty)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
break
end
-- set processedProperty
local processedProperty
local raw = false -- if the value is wanted raw
if isValueInTable(paramName, citeConfig[citeType]["raw-value-params"] or {}) then
raw = true
end
if isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
-- Multiple values may be given.
processedProperty = self:getReferenceDetails(statement.snaks, refProperty, raw, self.linked, true) -- anyLang = true
else
-- If multiple values are given, all but the first suitable one are discarted.
processedProperty = {self:getReferenceDetail(statement.snaks, refProperty, raw, self.linked and (statement.snaks[refProperty][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #processedProperty == 0 then
break
end
referenceEmpty = false
-- add an empty entry to candidateValuesAndPriorities, if there isn't one already
if not candidateValuesAndPriorities[paramName] then
candidateValuesAndPriorities[paramName] = {}
end
-- find the priority of refProperty
local thisPropertyPriority = -1
local thisParamPrioritization = citeConfig[citeType]["prioritization"][paramName]
if thisParamPrioritization then
for i_priority, i_property in ipairs(thisParamPrioritization) do
if i_property == refProperty then
thisPropertyPriority = i_priority
end
end
end
for _, propertyValue in pairs(processedProperty) do
table.insert(
candidateValuesAndPriorities[paramName],
{prefix .. propertyValue, thisPropertyPriority}
)
end
until true
end
-- fill candidateParams[citeType]
if not citeErrors[citeType] then
local compareValuePriorities = function(pair1, pair2)
if pair1[2] == -1 and pair2[2] ~= -1 then
return false
end
if pair1[2] ~= -1 and pair2[2] == -1 then
return true
end
return pair1[2] < pair2[2]
end
-- fill candidateParams[citeType][paramName] for each used param
for paramName, _ in pairs(candidateValuesAndPriorities) do
table.sort(candidateValuesAndPriorities[paramName], compareValuePriorities)
candidateParams[citeType][paramName] = {}
for _, valuePriorityPair in ipairs(candidateValuesAndPriorities[paramName]) do
table.insert(candidateParams[citeType][paramName], valuePriorityPair[1])
end
end
end
end
-- handle additional properties
for refProperty in pairs(additionalProcessedProperties) do
for _, citeType in ipairs(citeTypes) do
repeat
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
local paramName = citeParamMapping[citeType][refProperty]
-- handle unknown properties in the reference
if not paramName then
-- Skip this additional property, but do not cause an error.
break
end
if paramName == "" then
break
end
referenceEmpty = false
if not candidateParams[citeType][paramName] then
candidateParams[citeType][paramName] = {}
end
for _, propertyValue in pairs(additionalProcessedProperties[refProperty]) do
table.insert(candidateParams[citeType][paramName], propertyValue)
end
until true
end
end
-- fill citeParams
for _, citeType in ipairs(citeTypes) do
for paramName, paramValues in pairs(candidateParams[citeType]) do
if #paramValues == 1 or not isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
citeParams[citeType][paramName] = paramValues[1]
else
-- There is more than one value for this parameter - the values will
-- go into separate numbered parameters (e.g. "author1", "author2")
for paramNum, paramValue in pairs(paramValues) do
citeParams[citeType][paramName .. paramNum] = paramValue
end
end
end
end
-- handle missing mandatory parameters for the templates
for _, citeType in ipairs(citeTypes) do
for _, requiredCiteParam in pairs(citeConfig[citeType]["mandatory-params"] or {}) do
if not citeParams[citeType][requiredCiteParam] then -- The required param is not present.
if citeErrors[citeType] then -- Do not override the previous error, if it exists.
break
end
local error_message = errorText("missing-mandatory-param", requiredCiteParam)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
end
end
end
local citeTypeToUse = nil
-- choose the output template
for _, citeType in ipairs(citeTypes) do
if not citeErrors[citeType] then
citeTypeToUse = citeType
break
end
end
-- set refContent
local refContent = ""
if citeTypeToUse then
local templateToUse = citeConfig[citeTypeToUse]["template"]
local paramsToUse = citeParams[citeTypeToUse]
if not templateToUse or templateToUse == "" then
throwError("no-such-reference-template", tostring(templateToUse), i18nPath, citeTypeToUse)
end
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(paramsToUse) do
refContent = refContent .. "|" .. i .. "=" .. v
end
refContent = "{{" .. templateToUse .. refContent .. "}}"
else
xpcall(
function () refContent = mw.getCurrentFrame():expandTemplate{title=templateToUse, args=paramsToUse} end,
function () throwError("no-such-reference-template", templateToUse, i18nPath, citeTypeToUse) end
)
end
-- If the citation couldn't be displayed using any template, but is not empty (barring ignored propeties), throw an error.
elseif not referenceEmpty then
refContent = errorText("malformed-reference-header")
for _, citeType in ipairs(citeTypes) do
refContent = refContent .. errorText("template-failure-reason", citeConfig[citeType]["template"], citeErrors[citeType])
end
refContent = refContent .. errorText("malformed-reference-footer")
end
-- wrap refContent
local ref = {}
if refContent ~= "" then
ref = {refContent}
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
ref.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['version']) + version)
end
return {ref}
else
return {}
end
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
local valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
j5a6l03tjwodgrvfnv3lb4x5up93wlv
803563
799218
2026-07-30T00:16:24Z
SM7
3953
55 revisions imported from [[:en:Module:Wd]]
799218
Scribunto
text/plain
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
require("strict")
local p = {}
local module_arg = ...
local i18n
local i18nPath
local function loadI18n(aliasesP, frame)
local title
if frame then
-- current module invoked by page/template, get its title from frame
title = frame:getTitle()
else
-- current module included by other module, get its title from ...
title = module_arg
end
if not i18n then
i18nPath = title .. "/i18n"
i18n = require(i18nPath).init(aliasesP)
end
end
p.claimCommands = {
property = "property",
properties = "properties",
qualifier = "qualifier",
qualifiers = "qualifiers",
reference = "reference",
references = "references"
}
p.generalCommands = {
label = "label",
title = "title",
description = "description",
alias = "alias",
aliases = "aliases",
badge = "badge",
badges = "badges"
}
p.flags = {
linked = "linked",
short = "short",
raw = "raw",
multilanguage = "multilanguage",
unit = "unit",
-------------
preferred = "preferred",
normal = "normal",
deprecated = "deprecated",
best = "best",
future = "future",
current = "current",
former = "former",
edit = "edit",
editAtEnd = "edit@end",
mdy = "mdy",
single = "single",
sourced = "sourced"
}
p.args = {
eid = "eid",
page = "page",
date = "date",
globalSiteId = "globalSiteId"
}
local aliasesP = {
coord = "P625",
-----------------------
image = "P18",
author = "P50",
authorNameString = "P2093",
publisher = "P123",
importedFrom = "P143",
wikimediaImportURL = "P4656",
statedIn = "P248",
pages = "P304",
language = "P407",
hasPart = "P527",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
sectionVerseOrParagraph = "P958",
archiveURL = "P1065",
title = "P1476",
formatterURL = "P1630",
quote = "P1683",
shortName = "P1813",
definingFormula = "P2534",
archiveDate = "P2960",
inferredFrom = "P3452",
typeOfReference = "P3865",
column = "P3903",
subjectNamedAs = "P1810",
wikidataProperty = "P1687",
publishedIn = "P1433",
lastUpdate = "P5017"
}
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
local parameters = {
property = "%p",
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
}
local formats = {
property = "%p[%s][%r]",
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
local hookNames = { -- {level_1, level_2}
[parameters.property] = {"getProperty"},
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
-- default value objects, should NOT be mutated but instead copied
local defaultSeparators = {
["sep"] = {" "},
["sep%s"] = {","},
["sep%q"] = {"; "},
["sep%q\\d"] = {", "},
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
return id
end
local function errorText(code, ...)
local text = i18n["errors"][code]
if arg then text = mw.ustring.format(text, unpack(arg)) end
return text
end
local function throwError(errorMessage, ...)
error(errorText(errorMessage, unpack(arg)))
end
local function replaceDecimalMark(num)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
end
local function padZeros(num, numDigits)
local numZeros
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for _ = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
local function replaceSpecialChar(chr)
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
local function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
-- with the actual amount and to simply copy tables
local function copyTable(tIn)
if not tIn then
return nil
end
local tOut = {}
for i, v in pairs(tIn) do
tOut[i] = v
end
return tOut
end
-- used to merge output arrays together;
-- note that it currently mutates the first input array
local function mergeArrays(a1, a2)
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end
return a1
end
local function split(str, del)
local out = {}
local i, j = str:find(del)
if i and j then
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end
return out
end
local function parseWikidataURL(url)
local id
if url:match('^http[s]?://') then
id = split(url, "Q")
if id[2] then
return "Q" .. id[2]
end
end
return nil
end
local function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
if dateStr == nil then
return parts[1], parts[2], parts[3] -- year, month, day
end
-- 'T' for snak values, '/' for outputs with '/Julian' attached
i, j = dateStr:find("[T/]")
if i then
dateStr = dateStr:sub(1, i-1)
end
local from = 1
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
index = 1
ptr = 1
i, j = dateStr:find("-", from)
if i then
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10) -- explicitly give base 10 to prevent error
if parts[index] == -0 then
parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
end
if precision == "y" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
i, j = dateStr:find("-", ptr)
if i then
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
if precision == "m" then
-- we're done
return parts[1], parts[2], parts[3] -- year, month, day
end
index = index + 1
ptr = i + 1
end
end
if dateStr:sub(ptr) ~= "" then
-- day if we have month, month if we have year, or year
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
return parts[1], parts[2], parts[3] -- year, month, day
end
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1
if aY < bY then
return true
end
if aY > bY then
return false
end
if aM < bM then
return true
end
if aM > bM then
return false
end
if aD < bD then
return true
end
return false
end
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
local function alwaysTrue()
return true
end
-- The following function parses a format string.
--
-- The example below shows how a parsed string is structured in memory.
-- Variables other than 'str' and 'child' are left out for clarity's sake.
--
-- Example:
-- "A %p B [%s[%q1]] C [%r] D"
--
-- Structure:
-- [
-- {
-- str = "A "
-- },
-- {
-- str = "%p"
-- },
-- {
-- str = " B ",
-- child =
-- [
-- {
-- str = "%s",
-- child =
-- [
-- {
-- str = "%q1"
-- }
-- ]
-- }
-- ]
-- },
-- {
-- str = " C ",
-- child =
-- [
-- {
-- str = "%r"
-- }
-- ]
-- },
-- {
-- str = " D"
-- }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}
local function newObject(array)
local obj = {} -- new object
obj.str = ""
array[#array + 1] = obj -- array{object}
obj.parent = array
return obj
end
local function endParam()
if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil
esc = false
param = 0
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
endParam()
esc = true
elseif chr == '%' then
endParam()
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end
prev = nil
end
endParam()
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
local function sortOnRank(claims)
local rankPos
local ranks = {{}, {}, {}, {}} -- preferred, normal, deprecated, (default)
local sorted = {}
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
return sorted
end
local function isValueInTable(searchedItem, inputTable)
for _, item in pairs(inputTable) do
if item == searchedItem then
return true
end
end
return false
end
local Config = {}
-- allows for recursive calls
function Config:new()
local cfg = {}
setmetatable(cfg, self)
self.__index = self
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.inSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
cfg.siteID = mw.wikibase.getGlobalSiteId()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
end
local State = {}
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self
stt.conf = cfg
stt.type = type
stt.results = {}
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
return stt
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return ""
end
end
id = id:upper() -- just to be sure
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label = mw.wikibase.getLabel(id)
end
end
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end
return label
end
function Config:getEditIcon()
local value = ""
local prefix = ""
local front = " "
local back = ""
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode
if self.propertyID then
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
end
value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. "]]"
return front .. value .. back
end
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
itemID = parseWikidataURL(unit)
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)
if label ~= "" then
return space .. label
end
end
end
return ""
end
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" .. datavalue .. "]]"
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end
return mw.getCurrentFrame():extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)
return "[" .. url .. " " .. datavalue .. "]"
else
return datavalue
end
else
return datavalue
end
elseif datatype == 'monolingualtext' then
if anyLang or datavalue['language'] == self.langCode then
return datavalue['text']
else
return nil
end
elseif datatype == 'quantity' then
local value = ""
local unit
if not unitOnly then
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")
if raw then
return value
end
-- replace decimal mark based on locale
value = replaceDecimalMark(value)
-- add delimiters for readability
value = i18n.addDelimiters(value)
end
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif datatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
local sign = 1
local prefix = ""
local suffix = ""
local mayAddCalendar = false
local calendar = ""
local precision = datavalue['precision']
if precision == 11 then
p = "d"
elseif precision == 10 then
p = "m"
else
p = "y"
yFactor = 10^(9-precision)
end
y, m, d = parseDate(datavalue['time'], p)
if y < 0 then
sign = -1
y = y * sign
end
-- if precision is tens/hundreds/thousands/millions/billions of years
if precision <= 8 then
yDiv = y / yFactor
-- if precision is tens/hundreds/thousands of years
if precision >= 6 then
mayAddCalendar = true
if precision <= 7 then
-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
yRound = math.ceil(yDiv)
if not raw then
if precision == 6 then
suffix = i18n['datetime']['suffixes']['millennium']
else
suffix = i18n['datetime']['suffixes']['century']
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
yRound = (yRound - 1) * yFactor + 1
end
else
-- precision == 8
-- round decades down (e.g. 2010s)
yRound = math.floor(yDiv) * yFactor
if not raw then
prefix = i18n['datetime']['prefixes']['decade-period']
suffix = i18n['datetime']['suffixes']['decade-period']
end
end
if raw and sign < 0 then
-- if BCE then compensate for "counting backwards"
-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
yRound = yRound + yFactor - 1
end
else
local yReFactor, yReDiv, yReRound
-- round to nearest for tens of thousands of years or more
yRound = math.floor(yDiv + 0.5)
if yRound == 0 then
if precision <= 2 and y ~= 0 then
yReFactor = 1e6
yReDiv = y / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years only if we have a whole number of them
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
if yRound == 0 then
-- otherwise, take the unrounded (original) number of years
precision = 5
yFactor = 1
yRound = y
mayAddCalendar = true
end
end
if precision >= 1 and y ~= 0 then
yFull = yRound * yFactor
yReFactor = 1e9
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to billions of years if we're in that range
precision = 0
yFactor = yReFactor
yRound = yReRound
else
yReFactor = 1e6
yReDiv = yFull / yReFactor
yReRound = math.floor(yReDiv + 0.5)
if yReDiv == yReRound then
-- change precision to millions of years if we're in that range
precision = 3
yFactor = yReFactor
yRound = yReRound
end
end
end
if not raw then
if precision == 3 then
suffix = i18n['datetime']['suffixes']['million-years']
elseif precision == 0 then
suffix = i18n['datetime']['suffixes']['billion-years']
else
yRound = yRound * yFactor
if yRound == 1 then
suffix = i18n['datetime']['suffixes']['year']
else
suffix = i18n['datetime']['suffixes']['years']
end
end
else
yRound = yRound * yFactor
end
end
else
yRound = y
mayAddCalendar = true
end
if mayAddCalendar then
calendarID = parseWikidataURL(datavalue['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
if not raw then
if link then
calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")"
else
calendar = " ("..i18n['datetime']['julian']..")"
end
else
calendar = "/"..i18n['datetime']['julian']
end
end
end
if not raw then
local ce = nil
if sign < 0 then
ce = i18n['datetime']['BCE']
elseif precision <= 5 then
ce = i18n['datetime']['CE']
end
if ce then
if link then
ce = buildWikilink(i18n['datetime']['common-era'], ce)
end
suffix = suffix .. " " .. ce
end
value = tostring(yRound)
if m then
dateStr = self.langObj:formatDate("F", "1-"..m.."-1")
if d then
if self.mdyDate then
dateStr = dateStr .. " " .. d .. ","
else
dateStr = d .. " " .. dateStr
end
end
value = dateStr .. " " .. value
end
value = prefix .. value .. suffix .. calendar
else
value = padZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
value = value .. calendar
end
return value
elseif datatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo (v4.0.1)
local precision, unitsPerDegree, numDigits, strFormat, value, globe
local latitude, latConv, latValue, latLink
local longitude, lonConv, lonValue, lonLink
local latDirection, latDirectionN, latDirectionS, latDirectionEN
local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
local degSymbol, minSymbol, secSymbol, separator
local latDegrees = nil
local latMinutes = nil
local latSeconds = nil
local lonDegrees = nil
local lonMinutes = nil
local lonSeconds = nil
local latDegSym = ""
local latMinSym = ""
local latSecSym = ""
local lonDegSym = ""
local lonMinSym = ""
local lonSecSym = ""
local latDirectionEN_N = "N"
local latDirectionEN_S = "S"
local lonDirectionEN_E = "E"
local lonDirectionEN_W = "W"
if not raw then
latDirectionN = i18n['coord']['latitude-north']
latDirectionS = i18n['coord']['latitude-south']
lonDirectionE = i18n['coord']['longitude-east']
lonDirectionW = i18n['coord']['longitude-west']
degSymbol = i18n['coord']['degrees']
minSymbol = i18n['coord']['minutes']
secSymbol = i18n['coord']['seconds']
separator = i18n['coord']['separator']
else
latDirectionN = latDirectionEN_N
latDirectionS = latDirectionEN_S
lonDirectionE = lonDirectionEN_E
lonDirectionW = lonDirectionEN_W
degSymbol = "/"
minSymbol = "/"
secSymbol = "/"
separator = "/"
end
latitude = datavalue['latitude']
longitude = datavalue['longitude']
if latitude < 0 then
latDirection = latDirectionS
latDirectionEN = latDirectionEN_S
latitude = math.abs(latitude)
else
latDirection = latDirectionN
latDirectionEN = latDirectionEN_N
end
if longitude < 0 then
lonDirection = lonDirectionW
lonDirectionEN = lonDirectionEN_W
longitude = math.abs(longitude)
else
lonDirection = lonDirectionE
lonDirectionEN = lonDirectionEN_E
end
precision = datavalue['precision']
if not precision or precision <= 0 then
precision = 1 / 3600 -- precision not set (correctly), set to arcsecond
end
-- remove insignificant detail
latitude = math.floor(latitude / precision + 0.5) * precision
longitude = math.floor(longitude / precision + 0.5) * precision
if precision >= 1 - (1 / 60) and precision < 1 then
precision = 1
elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then
precision = 1 / 60
end
if precision >= 1 then
unitsPerDegree = 1
elseif precision >= (1 / 60) then
unitsPerDegree = 60
else
unitsPerDegree = 3600
end
numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
if numDigits <= 0 then
numDigits = tonumber("0") -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
end
strFormat = "%." .. numDigits .. "f"
if precision >= 1 then
latDegrees = strFormat:format(latitude)
lonDegrees = strFormat:format(longitude)
if not raw then
latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
else
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
end
else
latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
if precision >= (1 / 60) then
latMinutes = latConv
lonMinutes = lonConv
else
latSeconds = latConv
lonSeconds = lonConv
latMinutes = math.floor(latSeconds / 60)
lonMinutes = math.floor(lonSeconds / 60)
latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
if not raw then
latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
else
latSecSym = latSeconds .. secSymbol
lonSecSym = lonSeconds .. secSymbol
end
end
latDegrees = math.floor(latMinutes / 60)
lonDegrees = math.floor(lonMinutes / 60)
latDegSym = latDegrees .. degSymbol
lonDegSym = lonDegrees .. degSymbol
latMinutes = latMinutes - (latDegrees * 60)
lonMinutes = lonMinutes - (lonDegrees * 60)
if precision >= (1 / 60) then
latMinutes = strFormat:format(latMinutes)
lonMinutes = strFormat:format(lonMinutes)
if not raw then
latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
else
latMinSym = latMinutes .. minSymbol
lonMinSym = lonMinutes .. minSymbol
end
end
latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
value = latValue .. separator .. lonValue
if link then
globe = parseWikidataURL(datavalue['globe'])
if globe then
globe = mw.wikibase.getLabelByLang(globe, "en"):lower()
else
globe = "earth"
end
latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
return value
elseif datatype == 'wikibase-entityid' then
local label
local itemID = datavalue['numeric-id']
if subtype == 'wikibase-item' then
itemID = "Q" .. itemID
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if label == "" then
label = nil
end
return label
else
return '<strong class="error">' .. errorText('unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
else
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
else
return i18n['values']['none']
end
else
return nil
end
end
function Config:getSingleRawQualifier(claim, qualifierID)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
if qualifiers and qualifiers[1] then
return self:getValue(qualifiers[1], true) -- raw = true
else
return nil
end
end
function Config:snakEqualsValue(snak, value)
local snakValue = self:getValue(snak, true) -- raw = true
if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
return snakValue == value
end
function Config:setRank(rank)
local rankPos
if rank == p.flags.best then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
return
end
if rank:sub(1,9) == p.flags.preferred then
rankPos = 1
elseif rank:sub(1,6) == p.flags.normal then
rankPos = 2
elseif rank:sub(1,10) == p.flags.deprecated then
rankPos = 3
else
return
end
-- one of the rank flags was given, check if another one was given before
if not self.flagRank then
self.ranks = {false, false, false} -- no other rank flag given before, so unset ranks
self.bestRank = self.flagBest -- unsets bestRank only if 'best' flag was not given before
self.flagRank = true -- mark that a rank flag was given
end
if rank:sub(-1) == "+" then
for i = rankPos, 1, -1 do
self.ranks[i] = true
end
elseif rank:sub(-1) == "-" then
for i = rankPos, #self.ranks do
self.ranks[i] = true
end
else
self.ranks[rankPos] = true
end
end
function Config:setPeriod(period)
local periodPos
if period == p.flags.future then
periodPos = 1
elseif period == p.flags.current then
periodPos = 2
elseif period == p.flags.former then
periodPos = 3
else
return
end
-- one of the period flags was given, check if another one was given before
if not self.flagPeriod then
self.periods = {false, false, false} -- no other period flag given before, so unset periods
self.flagPeriod = true -- mark that a period flag was given
end
self.periods[periodPos] = true
end
function Config:qualifierMatches(claim, id, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for _, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
function Config:rankMatches(rankPos)
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
else
return self.ranks[rankPos]
end
end
function Config:timeMatches(claim)
local startTime = nil
local startTimeY = nil
local startTimeM = nil
local startTimeD = nil
local endTime = nil
local endTimeY = nil
local endTimeM = nil
local endTimeD = nil
if self.periods[1] and self.periods[2] and self.periods[3] then
-- any time
return true
end
startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
end
if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
-- invalidate end time if it precedes start time
endTimeY = nil
endTimeM = nil
endTimeD = nil
end
if self.periods[1] then
-- future
if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
return true
end
end
if self.periods[2] then
-- current
if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
(endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
return true
end
end
if self.periods[3] then
-- former
if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
return true
end
end
return false
end
function Config:processFlag(flag)
if not flag then
return false
end
if flag == p.flags.linked then
self.curState.linked = true
return true
elseif flag == p.flags.raw then
self.curState.rawValue = true
if self.curState == self.states[parameters.reference] then
-- raw reference values end with periods and require a separator (other than none)
self.separators["sep%r"][1] = {" "}
end
return true
elseif flag == p.flags.short then
self.curState.shortName = true
return true
elseif flag == p.flags.multilanguage then
self.curState.anyLanguage = true
return true
elseif flag == p.flags.unit then
self.curState.unitOnly = true
return true
elseif flag == p.flags.mdy then
self.mdyDate = true
return true
elseif flag == p.flags.single then
self.singleClaim = true
return true
elseif flag == p.flags.sourced then
self.sourcedOnly = true
return true
elseif flag == p.flags.edit then
self.editable = true
return true
elseif flag == p.flags.editAtEnd then
self.editable = true
self.editAtEnd = true
return true
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
self:setRank(flag)
return true
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
self:setPeriod(flag)
return true
elseif flag == "" then
-- ignore empty flags and carry on
return true
else
return false
end
end
function Config:processFlagOrCommand(flag)
local param = ""
if not flag then
return false
end
if flag == p.claimCommands.property or flag == p.claimCommands.properties then
param = parameters.property
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
self.states.qualifiersCount = self.states.qualifiersCount + 1
param = parameters.qualifier .. self.states.qualifiersCount
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])}
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
param = parameters.reference
else
return self:processFlag(flag)
end
if self.states[param] then
return false
end
-- create a new state for each command
self.states[param] = State:new(self, param)
-- use "%x" as the general parameter name
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p"
-- set the separator
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately
if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
function Config:processSeparators(args)
local sep
for i, v in pairs(self.separators) do
if args[i] then
sep = replaceSpecialChars(args[i])
if sep ~= "" then
self.separators[i][1] = {sep}
else
self.separators[i][1] = nil
end
end
end
end
function Config:setFormatAndSeparators(state, parsedFormat)
state.parsedFormat = parsedFormat
state.separator = self.separators["sep"]
state.movSeparator = self.separators["sep"..parameters.separator]
state.puncMark = self.separators["punc"]
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
function State:claimMatches(claim)
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
if self.conf.propertyValue then
matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
else
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
-- check if the claim's rank and time period match
rankPos = rankTable[claim.rank] or 4
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim)) -- prefetches and caches references
end
return matches, rankPos
end
function State:out()
local result -- collection of arrays with value objects
local valuesArray -- array with value objects
local sep = nil -- value object
local out = {} -- array with value objects
local function walk(formatTable, result)
local valuesArray = {} -- array with value objects
for i, v in pairs(formatTable.req) do
if not result[i] or not result[i][1] then
-- we've got no result for a parameter that is required on this level,
-- so skip this level (and its children) by returning an empty result
return {}
end
end
for _, v in ipairs(formatTable) do
if v.param then
valuesArray = mergeArrays(valuesArray, result[v.str])
elseif v.str ~= "" then
valuesArray[#valuesArray + 1] = {v.str}
end
if v.child then
valuesArray = mergeArrays(valuesArray, walk(v.child, result))
end
end
return valuesArray
end
-- iterate through the results from back to front, so that we know when to add separators
for i = #self.results, 1, -1 do
result = self.results[i]
-- if there is already some output, then add the separators
if #out > 0 then
sep = self.separator[1] -- fixed separator
result[parameters.separator] = {self.movSeparator[1]} -- movable separator
else
sep = nil
result[parameters.separator] = {self.puncMark[1]} -- optional punctuation mark
end
valuesArray = walk(self.parsedFormat, result)
if #valuesArray > 0 then
if sep then
valuesArray[#valuesArray + 1] = sep
end
out = mergeArrays(valuesArray, out)
end
end
-- reset state before next iteration
self.results = {}
return out
end
-- level 1 hook
function State:getProperty(claim)
local value = {self:getValue(claim.mainsnak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getQualifiers(claim, param)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
if qualifiers then
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1}) -- pass qualifier state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getQualifier(snak)
local value = {self:getValue(snak)} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getAllQualifiers(claim, param, result, hooks)
local out = {} -- array with value objects
local sep = self.conf.separators["sep"..parameters.qualifier][1] -- value object
-- iterate through the output of the separate "qualifier(s)" commands
for i = 1, self.conf.states.qualifiersCount do
-- if a hook has not been called yet, call it now
if not result[parameters.qualifier..i] then
self:callHook(parameters.qualifier..i, hooks, claim, result)
end
-- if there is output for this particular "qualifier(s)" command, then add it
if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
-- if there is already some output, then add the separator
if #out > 0 and sep then
out[#out + 1] = sep
end
out = mergeArrays(out, result[parameters.qualifier..i])
end
end
return out
end
-- level 1 hook
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1}) -- pass reference state with level 2 hook
else
return {} -- return empty array
end
end
-- level 2 hook
function State:getReference(statement)
local citeParamMapping = i18n['cite']['param-mapping']
local citeConfig = i18n['cite']['config']
local citeTypes = i18n['cite']['output-types']
-- will hold rendered properties of the reference which are not directly from statement.snaks,
-- Namely, is URL generated from an external ID.
local additionalProcessedProperties = {}
-- for each citation type, there will be an associative array that associates lists of rendered properties
-- to citation-template parameters
local candidateParams = {}
-- like above, but only associates one rendered property to each parameter; if the above variable
-- contains more strings for a parameter, the strings will be assigned to numbered params (e.g. "author1")
local citeParams = {}
local citeErrors = {}
local referenceEmpty = true -- will be set to false if at least one parameter is left unremoved
local version = 12 -- increment this each time the below logic is changed to avoid conflict errors
if not statement.snaks then
return {}
end
-- don't use bot-added references referencing Wikimedia projects or containing "inferred from" (such references are not usable on Wikipedia)
if statement.snaks[aliasesP.importedFrom] or statement.snaks[aliasesP.wikimediaImportURL] or statement.snaks[aliasesP.inferredFrom] then
return {}
end
-- don't include "type of reference"
if statement.snaks[aliasesP.typeOfReference] then
statement.snaks[aliasesP.typeOfReference] = nil
end
-- don't include "image" to prevent littering
if statement.snaks[aliasesP.image] then
statement.snaks[aliasesP.image] = nil
end
-- don't include "language" if it is equal to the local one
if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
statement.snaks[aliasesP.language] = nil
end
if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
-- "stated in" was given but "reference URL" was not.
-- get "Wikidata property" properties from the item in "stated in"
-- if any of the returned properties of the external-id datatype is in statement.snaks, generate a link from it and use the link in the reference
-- find the "Wikidata property" properties in the item from "stated in"
local wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
local tempLink = self:getReferenceDetail(statement.snaks, wikidataPropertyOfSource, false, true) -- not raw, linked
if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then -- getValue returned a URL in square brackets.
-- the link is in wiki markup, so strip the square brackets and the display text
-- gsub also returns another, discarted value, therefore the result is assigned to tempLink first
tempLink = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1")
additionalProcessedProperties[aliasesP.referenceURL] = {tempLink}
statement.snaks[wikidataPropertyOfSource] = nil
break
end
end
end
end
-- initialize candidateParams and citeParams
for _, citeType in ipairs(citeTypes) do
candidateParams[citeType] = {}
citeParams[citeType] = {}
end
-- fill candidateParams
for _, citeType in ipairs(citeTypes) do
-- This will contain value--priority pairs for each param name.
local candidateValuesAndPriorities = {}
-- fill candidateValuesAndPriorities
for refProperty in pairs(statement.snaks) do
if citeErrors[citeType] then
break
end
repeat -- just a simple wrapper to emulate "continue"
-- set mappingKey and prefix
local mappingKey
local prefix = ""
if statement.snaks[refProperty][1].datatype == 'external-id' then
mappingKey = "external-id"
prefix = self.conf:getLabel(refProperty)
if prefix ~= "" then
prefix = prefix .. " "
end
else
mappingKey = refProperty
end
local paramName = citeParamMapping[citeType][mappingKey]
-- skip properties with empty parameter name
if paramName == "" then
break -- skip this property for this value of citeType
end
-- handle unknown properties in the reference
if not paramName then
referenceEmpty = false
local error_message = errorText("unknown-property-in-ref", refProperty)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
break
end
-- set processedProperty
local processedProperty
local raw = false -- if the value is wanted raw
if isValueInTable(paramName, citeConfig[citeType]["raw-value-params"] or {}) then
raw = true
end
if isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
-- Multiple values may be given.
processedProperty = self:getReferenceDetails(statement.snaks, refProperty, raw, self.linked, true) -- anyLang = true
else
-- If multiple values are given, all but the first suitable one are discarted.
processedProperty = {self:getReferenceDetail(statement.snaks, refProperty, raw, self.linked and (statement.snaks[refProperty][1].datatype ~= 'url'), true)} -- link = true/false, anyLang = true
end
if #processedProperty == 0 then
break
end
referenceEmpty = false
-- add an empty entry to candidateValuesAndPriorities, if there isn't one already
if not candidateValuesAndPriorities[paramName] then
candidateValuesAndPriorities[paramName] = {}
end
-- find the priority of refProperty
local thisPropertyPriority = -1
local thisParamPrioritization = citeConfig[citeType]["prioritization"][paramName]
if thisParamPrioritization then
for i_priority, i_property in ipairs(thisParamPrioritization) do
if i_property == refProperty then
thisPropertyPriority = i_priority
end
end
end
for _, propertyValue in pairs(processedProperty) do
table.insert(
candidateValuesAndPriorities[paramName],
{prefix .. propertyValue, thisPropertyPriority}
)
end
until true
end
-- fill candidateParams[citeType]
if not citeErrors[citeType] then
local compareValuePriorities = function(pair1, pair2)
if pair1[2] == -1 and pair2[2] ~= -1 then
return false
end
if pair1[2] ~= -1 and pair2[2] == -1 then
return true
end
return pair1[2] < pair2[2]
end
-- fill candidateParams[citeType][paramName] for each used param
for paramName, _ in pairs(candidateValuesAndPriorities) do
table.sort(candidateValuesAndPriorities[paramName], compareValuePriorities)
candidateParams[citeType][paramName] = {}
for _, valuePriorityPair in ipairs(candidateValuesAndPriorities[paramName]) do
table.insert(candidateParams[citeType][paramName], valuePriorityPair[1])
end
end
end
end
-- handle additional properties
for refProperty in pairs(additionalProcessedProperties) do
for _, citeType in ipairs(citeTypes) do
repeat
-- skip if there already have been errors
if citeErrors[citeType] then
break
end
local paramName = citeParamMapping[citeType][refProperty]
-- handle unknown properties in the reference
if not paramName then
-- Skip this additional property, but do not cause an error.
break
end
if paramName == "" then
break
end
referenceEmpty = false
if not candidateParams[citeType][paramName] then
candidateParams[citeType][paramName] = {}
end
for _, propertyValue in pairs(additionalProcessedProperties[refProperty]) do
table.insert(candidateParams[citeType][paramName], propertyValue)
end
until true
end
end
-- fill citeParams
for _, citeType in ipairs(citeTypes) do
for paramName, paramValues in pairs(candidateParams[citeType]) do
if #paramValues == 1 or not isValueInTable(paramName, citeConfig[citeType]["numbered-params"] or {}) then
citeParams[citeType][paramName] = paramValues[1]
else
-- There is more than one value for this parameter - the values will
-- go into separate numbered parameters (e.g. "author1", "author2")
for paramNum, paramValue in pairs(paramValues) do
citeParams[citeType][paramName .. paramNum] = paramValue
end
end
end
end
-- handle missing mandatory parameters for the templates
for _, citeType in ipairs(citeTypes) do
for _, requiredCiteParam in pairs(citeConfig[citeType]["mandatory-params"] or {}) do
if not citeParams[citeType][requiredCiteParam] then -- The required param is not present.
if citeErrors[citeType] then -- Do not override the previous error, if it exists.
break
end
local error_message = errorText("missing-mandatory-param", requiredCiteParam)
assert(error_message) -- Should not be nil
citeErrors[citeType] = error_message
end
end
end
local citeTypeToUse = nil
-- choose the output template
for _, citeType in ipairs(citeTypes) do
if not citeErrors[citeType] then
citeTypeToUse = citeType
break
end
end
-- set refContent
local refContent = ""
if citeTypeToUse then
local templateToUse = citeConfig[citeTypeToUse]["template"]
local paramsToUse = citeParams[citeTypeToUse]
if not templateToUse or templateToUse == "" then
throwError("no-such-reference-template", tostring(templateToUse), i18nPath, citeTypeToUse)
end
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
for i, v in pairs(paramsToUse) do
refContent = refContent .. "|" .. i .. "=" .. v
end
refContent = "{{" .. templateToUse .. refContent .. "}}"
else
xpcall(
function () refContent = mw.getCurrentFrame():expandTemplate{title=templateToUse, args=paramsToUse} end,
function () throwError("no-such-reference-template", templateToUse, i18nPath, citeTypeToUse) end
)
end
-- If the citation couldn't be displayed using any template, but is not empty (barring ignored propeties), throw an error.
elseif not referenceEmpty then
refContent = errorText("malformed-reference-header")
for _, citeType in ipairs(citeTypes) do
refContent = refContent .. errorText("template-failure-reason", citeConfig[citeType]["template"], citeErrors[citeType])
end
refContent = refContent .. errorText("malformed-reference-footer")
end
-- wrap refContent
local ref = {}
if refContent ~= "" then
ref = {refContent}
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
ref.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['version']) + version)
end
return {ref}
else
return {}
end
end
-- gets a detail of one particular type for a reference
function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
local switchLang = anyLang
local value = nil
if not snaks[dType] then
return nil
end
-- if anyLang, first try the local language and otherwise any language
repeat
for _, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true) -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
return value
end
-- gets the details of one particular type for a reference
function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
local values = {}
if not snaks[dType] then
return {}
end
for _, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true) -- noSpecial = true
end
return values
end
-- level 1 hook
function State:getAlias(object)
local value = object.value
local title = nil
if value and self.linked then
if self.conf.entityID:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(self.conf.entityID)
elseif self.conf.entityID:sub(1,1) == "P" then
title = "d:Property:" .. self.conf.entityID
end
if title then
value = buildWikilink(title, value)
end
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
-- level 1 hook
function State:getBadge(value)
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
if value == "" then
value = nil
end
value = {value} -- create one value object
if #value > 0 then
return {value} -- wrap the value object in an array and return it
else
return {} -- return empty array if there was no value
end
end
function State:callHook(param, hooks, statement, result)
-- call a parameter's hook if it has been defined and if it has not been called before
if not result[param] and hooks[param] then
local valuesArray = self[hooks[param]](self, statement, param, result, hooks) -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {} -- an empty array to indicate that we've tried this hook already
return true -- miss == true
end
end
return false
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for _, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0} -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in pairs(formatTable.req) do
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for _, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
return true
end
if v2.child then
walk(v2.child)
end
end
return true
end
gotRequired = walk(self.parsedFormat)
-- only append the result if we got values for all required parameters on the root level
if gotRequired then
-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
if rankPos and self.conf.foundRank > rankPos then
self.conf.foundRank = rankPos
end
-- append the result
self.results[#self.results + 1] = result
-- break if we only need a single value
if self.singleValue then
break
end
end
end
end
return self:out()
end
local function getEntityId(arg, eid, page, allowOmitPropPrefix, globalSiteId)
local id = nil
local prop = nil
if arg then
if arg:sub(1,1) == ":" then
page = arg
eid = nil
elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then
eid = arg
page = nil
else
prop = arg
end
end
if eid then
if eid:sub(1,9):lower() == "property:" then
id = replaceAlias(mw.text.trim(eid:sub(10)))
if id:sub(1,1):upper() ~= "P" then
id = ""
end
else
id = replaceAlias(eid)
end
elseif page then
if page:sub(1,1) == ":" then
page = mw.text.trim(page:sub(2))
end
id = mw.wikibase.getEntityIdForTitle(page, globalSiteId) or ""
end
if not id then
id = mw.wikibase.getEntityIdForCurrentPage() or ""
end
id = id:upper()
if not mw.wikibase.isValidEntityId(id) then
id = ""
end
return id, prop
end
local function nextArg(args)
local arg = args[args.pointer]
if arg then
args.pointer = args.pointer + 1
return mw.text.trim(arg)
else
return nil
end
end
local function claimCommand(args, funcName)
local cfg = Config:new()
cfg:processFlagOrCommand(funcName) -- process first command (== function name)
local lastArg, parsedFormat, formatParams, claims, value
local hooks = {count = 0}
-- set the date if given;
-- must come BEFORE processing the flags
if args[p.args.date] then
cfg.atDate = {parseDate(args[p.args.date])}
cfg.periods = {false, true, false} -- change default time constraint to 'current'
end
-- process flags and commands
repeat
lastArg = nextArg(args)
until not cfg:processFlagOrCommand(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], false, args[p.args.globalSiteId])
if cfg.entityID == "" then
return "" -- we cannot continue without a valid entity ID
end
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if not cfg.propertyID then
cfg.propertyID = nextArg(args)
end
cfg.propertyID = replaceAlias(cfg.propertyID)
if not cfg.entity or not cfg.propertyID then
return "" -- we cannot continue without an entity or a property ID
end
cfg.propertyID = cfg.propertyID:upper()
if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
return "" -- there is no use to continue without any claims
end
claims = cfg.entity.claims[cfg.propertyID]
if cfg.states.qualifiersCount > 0 then
-- do further processing if "qualifier(s)" command was given
if #args - args.pointer + 1 > cfg.states.qualifiersCount then
-- claim ID or literal value has been given
cfg.propertyValue = nextArg(args)
end
for i = 1, cfg.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper()
end
elseif cfg.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
cfg.propertyValue = nextArg(args)
end
-- check for special property value 'somevalue' or 'novalue'
if cfg.propertyValue then
cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then
cfg.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
else
cfg.propertyValue = mw.text.trim(cfg.propertyValue)
end
end
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
elseif cfg.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
else
parsedFormat, formatParams = parseFormat(formats.qualifier)
end
elseif cfg.states[parameters.property] then -- "propert(y|ies)" command given
parsedFormat, formatParams = parseFormat(formats.property)
else -- "reference(s)" command given
parsedFormat, formatParams = parseFormat(formats.reference)
end
-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon
if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then
cfg.separators["sep"..parameters.separator][1] = {";"}
end
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
and not cfg.states[parameters.reference].rawValue then
cfg.separators["sep"][1] = nil
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent
if cfg.states.qualifiersCount == 1 then
cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"]
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(cfg.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not cfg.states[parameters.property] then
cfg.states[parameters.property] = State:new(cfg, parameters.property)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if cfg.singleClaim then
cfg.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if cfg.sourcedOnly and not cfg.states[parameters.reference] then
cfg:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to cfg.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " " -- single space represents 'somevalue'
end
cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
claims = sortOnRank(claims)
-- then iterate through the claims to collect values
value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property state with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if cfg.editable and value ~= "" then
value = value .. cfg:getEditIcon()
end
return value
end
local function generalCommand(args, funcName)
local cfg = Config:new()
cfg.curState = State:new(cfg)
local lastArg
local value = nil
repeat
lastArg = nextArg(args)
until not cfg:processFlag(lastArg)
-- get the entity ID from either the positional argument, the eid argument or the page argument
cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true, args[p.args.globalSiteId])
if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then
return "" -- we cannot continue without an entity
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
elseif funcName == p.generalCommands.title then
cfg.inSitelinks = true
if cfg.entityID:sub(1,1) == "Q" then
value = mw.wikibase.getSitelink(cfg.entityID)
end
if cfg.curState.linked and value then
value = buildWikilink(value)
end
elseif funcName == p.generalCommands.description then
value = mw.wikibase.getDescription(cfg.entityID)
else
local parsedFormat, formatParams
local hooks = {count = 0}
cfg.entity = mw.wikibase.getEntity(cfg.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
cfg.curState.singleValue = true
end
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
return "" -- there is no use to continue without any aliasses
end
local aliases = cfg.entity.aliases[cfg.langCode]
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
return "" -- there is no use to continue without any badges
end
local badges = cfg.entity.sitelinks[cfg.siteID].badges
cfg.inSitelinks = true
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
cfg:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
-- iterate to collect values
value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
end
end
value = value or ""
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
end
return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end
local f, args
loadI18n(aliasesP, frame)
-- get the parent frame to take the arguments that were passed to the wrapper template
frame = frame:getParent() or frame
if not frame.args[1] then
throwError("no-function-specified")
end
f = mw.text.trim(frame.args[1])
if f == "main" then
throwError("main-called-twice")
end
assert(p["_"..f], errorText('no-such-function', f))
-- copy arguments from immutable to mutable table
args = copyTable(frame.args)
-- remove the function name from the list
table.remove(args, 1)
return p["_"..f](args)
end
return p
j5a6l03tjwodgrvfnv3lb4x5up93wlv
Module:Wd/i18n
828
46674
803494
661827
2020-04-19T10:24:42Z
en>Thayts
0
Preparing update to remove inclusion of aliasesP
803494
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
-- load submodule "aliasesP" that lives next to this submodule
local aliasesP = mw.loadData((...):sub(1, (...):match('^.*()/') - 1).."/aliasesP")
local p = {
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '$1'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '$1' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "$1" does not exist' -- equal to the standard module error message
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["version"] = "2", -- increase this each time the below parameters are changed to avoid conflict errors
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author", -- existence of author1, author2, author3, etc. is assumed
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages" -- extra option
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
}
}
function p.getOrdinalSuffix(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
function p.addDelimiters(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
function p.init() return p end
return p
sam3lpkcgamwpznq8upvjw6d7qwd1lr
803495
803494
2020-04-19T10:39:31Z
en>Thayts
0
Finishing update
803495
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '$1'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '$1' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "$1" does not exist' -- equal to the standard module error message
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["version"] = "2", -- increase this each time the below parameters are changed to avoid conflict errors
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author", -- existence of author1, author2, author3, etc. is assumed
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages" -- extra option
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
o2jeg5pzdz7z1yp2ldi8unzt6gpedo5
803496
803495
2020-04-19T11:12:50Z
en>Thayts
0
803496
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '$1'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '$1' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "$1" does not exist' -- equal to the standard module error message
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["version"] = "2", -- increment this each time the below parameters are changed to avoid conflict errors
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author", -- existence of author1, author2, author3, etc. is assumed
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages" -- extra option
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
h70ieb0uxswts38nevjbf8jjyt60l0l
803497
803496
2023-05-06T16:56:37Z
en>Pppery
0
Per edit request
803497
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '$1'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '$1' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "$1" does not exist', -- equal to the standard module error message
["malformed-reference"] = "Error: Unable to display the reference properly. See [[Module:wd/doc#References|the documentation]] for details."
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["version"] = "3", -- increment this each time the below parameters are changed to avoid conflict errors
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author", -- existence of author1, author2, author3, etc. is assumed
[aliasesP.authorNameString] = "author",
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages" -- extra option
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
delp2h80l8nqa22xb63mor10yq2z7ld
803498
756518
2023-06-23T17:34:03Z
en>Trappist the monk
0
categorize reference errors;
803498
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '$1'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '$1' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "$1" does not exist', -- equal to the standard module error message
["malformed-reference"] = "Error: Unable to display the reference properly. See [[Module:wd/doc#References|the documentation]] for details.[[Category:Module:Wd reference errors]]"
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["version"] = "3", -- increment this each time the below parameters are changed to avoid conflict errors
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author", -- existence of author1, author2, author3, etc. is assumed
[aliasesP.authorNameString] = "author",
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages" -- extra option
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
jsazx48spsxh1tff7q1p8owi97r6jay
803499
803498
2023-07-10T18:07:36Z
en>Pppery
0
per edit request
803499
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '$1'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '$1' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "$1" does not exist', -- equal to the standard module error message
["malformed-reference"] = "Error: Unable to display the reference properly. See [[Module:wd/doc#References|the documentation]] for details.[[Category:Module:Wd reference errors]]"
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["version"] = "4", -- increment this each time the below parameters are changed to avoid conflict errors
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author", -- existence of author1, author2, author3, etc. is assumed
[aliasesP.authorNameString] = "author",
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages", -- extra option
[aliasesP.publishedIn] = "website"
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
jo23v6upx7wskntuutq3dsmiwz0xp63
803500
803499
2024-02-11T15:54:21Z
en>Sdkb
0
Mapping [[wikidata:Property:P958|Wikidata's "section, verse, paragraph, or clause" property]] to {{cite web}}'s "at" parameter, [[Module_talk:Wd#Error|per talk]]
803500
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '$1'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '$1' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "$1" does not exist', -- equal to the standard module error message
["malformed-reference"] = "Error: Unable to display the reference properly. See [[Module:wd/doc#References|the documentation]] for details.[[Category:Module:Wd reference errors]]"
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["version"] = "5", -- increment this each time the below parameters are changed to avoid conflict errors
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author", -- existence of author1, author2, author3, etc. is assumed
[aliasesP.authorNameString] = "author",
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages", -- extra option
[aliasesP.publishedIn] = "website",
[aliasesP.sectionVerseOrParagraph] = "at"
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
t28rz4790z52gadkxdwisqlpv6mjteu
803501
803500
2024-08-26T09:41:07Z
en>Janhrach
0
update from sandbox -- layout and configurability -- changes are *not* backward-compatible
803501
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '$1'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '$1' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "$1" does not exist', -- equal to the standard module error message
["malformed-reference"] = "Error: Unable to display the reference properly. See [[Module:wd/doc#References|the documentation]] for details.",
["no-such-reference-template"] = 'Error: template "%s", which is set in %s as the output template for the citation-output type "%s", does not exist'
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["version"] = "6", -- increment this each time the below parameters are changed to avoid conflict errors
["output-types"] = {"web", "q"}, -- In this order, the output types will be tried
["param-mapping"] = {
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author",
[aliasesP.authorNameString] = "author",
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages", -- extra option
[aliasesP.publishedIn] = "website",
[aliasesP.sectionVerseOrParagraph] = "at"
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
},
["config"] = {
-- supported fields:
-- - template: name of the template used for output
-- - numbered-params: citation params accepting an arbitrary number of values by numbering the params (e.g. author1, author2)
-- - raw-value-params: params taking a raw value (which means the property is rendered with getValue with raw=true)
-- - mandatory-params: params that are required be in the template call (after potentially appending numbers to params listed in numbered-params)
-- Leaving out the "template" field causes the output type to be ignored.
["web"] = {
["template"] = "Cite web",
["numbered-params"] = {"author"},
["mandatory-params"] = {"url"}
},
["q"] = {
["template"] = "Cite Q",
["raw-value-params"] = {"1"}, -- the first, unnamed parameter of CiteQ takes a QID, not the name of the item cited
["mandatory-params"] = {"1"}
}
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
ftulxnol0x4pzd6857feese1yizwn1x
803502
803501
2024-08-27T16:51:56Z
en>Janhrach
0
I do not know how, but I accidentally removed the tracking category. Fixing.
803502
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '$1'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '$1' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "$1" does not exist', -- equal to the standard module error message
["malformed-reference"] = "Error: Unable to display the reference properly. See [[Module:wd/doc#References|the documentation]] for details.[[Category:Module:Wd reference errors]]",
["no-such-reference-template"] = 'Error: template "%s", which is set in %s as the output template for the citation-output type "%s", does not exist'
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["version"] = "6", -- increment this each time the below parameters are changed to avoid conflict errors
["output-types"] = {"web", "q"}, -- In this order, the output types will be tried
["param-mapping"] = {
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author",
[aliasesP.authorNameString] = "author",
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages", -- extra option
[aliasesP.publishedIn] = "website",
[aliasesP.sectionVerseOrParagraph] = "at"
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
},
["config"] = {
-- supported fields:
-- - template: name of the template used for output
-- - numbered-params: citation params accepting an arbitrary number of values by numbering the params (e.g. author1, author2)
-- - raw-value-params: params taking a raw value (which means the property is rendered with getValue with raw=true)
-- - mandatory-params: params that are required be in the template call (after potentially appending numbers to params listed in numbered-params)
-- Leaving out the "template" field causes the output type to be ignored.
["web"] = {
["template"] = "Cite web",
["numbered-params"] = {"author"},
["mandatory-params"] = {"url"}
},
["q"] = {
["template"] = "Cite Q",
["raw-value-params"] = {"1"}, -- the first, unnamed parameter of CiteQ takes a QID, not the name of the item cited
["mandatory-params"] = {"1"}
}
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
43r1q7uzi8u36rmnl0nou4mjq944kjk
803503
803502
2024-09-02T16:58:45Z
en>Janhrach
0
companion edit to the main-module one (desc.: "citation error message improvement and related internal tweaks")
803503
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["version"] = "7", -- increment this each time the below parameters are changed to avoid reference conflict errors
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '%s'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '%s' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "%s" does not exist', -- equal to the standard module error message
["no-such-reference-template"] = 'Error: template "%s", which is set in %s as the output template for the citation-output type "%s", does not exist',
-- Parts of the error message signalling a malformed reference.
["malformed-reference-header"] = "<span style=\"color:#dd3333\">\nError: Unable to display the reference from Wikidata properly. Technical details:\n",
["malformed-reference-footer"] = "See [[Module:wd/doc#References|the documentation]] for further details.\n</span>\n[[Category:Module:Wd reference errors]]",
["template-failure-reason"] = "* Reason for the failure of {{tl|%s}}: %s\n",
["missing-mandatory-param"] = 'The output template call would miss the mandatory parameter <code>%s</code>.',
["unknown-property-in-ref"] = 'The Wikidata reference contains the property {{property|%s}}, which is not assigned to any parameter of this template.'
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["output-types"] = {"web", "q"}, -- In this order, the output types will be tried
["param-mapping"] = {
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author",
[aliasesP.authorNameString] = "author",
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages", -- extra option
[aliasesP.publishedIn] = "website",
[aliasesP.sectionVerseOrParagraph] = "at"
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
},
["config"] = {
-- supported fields:
-- - template: name of the template used for output
-- - numbered-params: citation params accepting an arbitrary number of values by numbering the params (e.g. author1, author2)
-- - raw-value-params: params taking a raw value (which means the property is rendered with getValue with raw=true)
-- - mandatory-params: params that are required be in the template call (after potentially appending numbers to params listed in numbered-params)
-- Leaving out the "template" field causes the output type to be ignored.
["web"] = {
["template"] = "Cite web",
["numbered-params"] = {"author"},
["mandatory-params"] = {"url"}
},
["q"] = {
["template"] = "Cite Q",
["raw-value-params"] = {"1"}, -- the first, unnamed parameter of CiteQ takes a QID, not the name of the item cited
["mandatory-params"] = {"1"}
}
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
9x26xqgg01idb8drjyqhb64or1txdtn
803504
803503
2024-10-05T13:16:59Z
en>Janhrach
0
add publication-place for cite web
803504
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["version"] = "8", -- increment this each time the below parameters are changed to avoid reference conflict errors
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '%s'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '%s' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "%s" does not exist', -- equal to the standard module error message
["no-such-reference-template"] = 'Error: template "%s", which is set in %s as the output template for the citation-output type "%s", does not exist',
-- Parts of the error message signalling a malformed reference.
["malformed-reference-header"] = "<span style=\"color:#dd3333\">\nError: Unable to display the reference from Wikidata properly. Technical details:\n",
["malformed-reference-footer"] = "See [[Module:wd/doc#References|the documentation]] for further details.\n</span>\n[[Category:Module:Wd reference errors]]",
["template-failure-reason"] = "* Reason for the failure of {{tl|%s}}: %s\n",
["missing-mandatory-param"] = 'The output template call would miss the mandatory parameter <code>%s</code>.',
["unknown-property-in-ref"] = 'The Wikidata reference contains the property {{property|%s}}, which is not assigned to any parameter of this template.'
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["output-types"] = {"web", "q"}, -- In this order, the output types will be tried
["param-mapping"] = {
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author",
[aliasesP.authorNameString] = "author",
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages", -- extra option
[aliasesP.publishedIn] = "website",
[aliasesP.sectionVerseOrParagraph] = "at",
[aliasesP.placeOfPublication] = "publication-place"
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
},
["config"] = {
-- supported fields:
-- - template: name of the template used for output
-- - numbered-params: citation params accepting an arbitrary number of values by numbering the params (e.g. author1, author2)
-- - raw-value-params: params taking a raw value (which means the property is rendered with getValue with raw=true)
-- - mandatory-params: params that are required be in the template call (after potentially appending numbers to params listed in numbered-params)
-- Leaving out the "template" field causes the output type to be ignored.
["web"] = {
["template"] = "Cite web",
["numbered-params"] = {"author"},
["mandatory-params"] = {"url"}
},
["q"] = {
["template"] = "Cite Q",
["raw-value-params"] = {"1"}, -- the first, unnamed parameter of CiteQ takes a QID, not the name of the item cited
["mandatory-params"] = {"1"}
}
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
0dgb7gzbanict0w1nisr7z00oncbn4m
803505
803504
2024-10-05T14:22:01Z
en>Janhrach
0
Undid revision [[Special:Diff/1249540561|1249540561]] by [[Special:Contributions/Janhrach|Janhrach]] ([[User talk:Janhrach|talk]])
803505
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["version"] = "7", -- increment this each time the below parameters are changed to avoid reference conflict errors
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '%s'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '%s' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "%s" does not exist', -- equal to the standard module error message
["no-such-reference-template"] = 'Error: template "%s", which is set in %s as the output template for the citation-output type "%s", does not exist',
-- Parts of the error message signalling a malformed reference.
["malformed-reference-header"] = "<span style=\"color:#dd3333\">\nError: Unable to display the reference from Wikidata properly. Technical details:\n",
["malformed-reference-footer"] = "See [[Module:wd/doc#References|the documentation]] for further details.\n</span>\n[[Category:Module:Wd reference errors]]",
["template-failure-reason"] = "* Reason for the failure of {{tl|%s}}: %s\n",
["missing-mandatory-param"] = 'The output template call would miss the mandatory parameter <code>%s</code>.',
["unknown-property-in-ref"] = 'The Wikidata reference contains the property {{property|%s}}, which is not assigned to any parameter of this template.'
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["output-types"] = {"web", "q"}, -- In this order, the output types will be tried
["param-mapping"] = {
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author",
[aliasesP.authorNameString] = "author",
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages", -- extra option
[aliasesP.publishedIn] = "website",
[aliasesP.sectionVerseOrParagraph] = "at"
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.retrieved] = "access-date"
}
},
["config"] = {
-- supported fields:
-- - template: name of the template used for output
-- - numbered-params: citation params accepting an arbitrary number of values by numbering the params (e.g. author1, author2)
-- - raw-value-params: params taking a raw value (which means the property is rendered with getValue with raw=true)
-- - mandatory-params: params that are required be in the template call (after potentially appending numbers to params listed in numbered-params)
-- Leaving out the "template" field causes the output type to be ignored.
["web"] = {
["template"] = "Cite web",
["numbered-params"] = {"author"},
["mandatory-params"] = {"url"}
},
["q"] = {
["template"] = "Cite Q",
["raw-value-params"] = {"1"}, -- the first, unnamed parameter of CiteQ takes a QID, not the name of the item cited
["mandatory-params"] = {"1"}
}
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
9x26xqgg01idb8drjyqhb64or1txdtn
803506
803505
2025-07-22T19:53:10Z
en>Janhrach
0
update from sandbox
803506
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["version"] = "8", -- increment this each time the below parameters are changed to avoid reference conflict errors
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '%s'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '%s' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "%s" does not exist', -- equal to the standard module error message
["no-such-reference-template"] = 'Error: template "%s", which is set in %s as the output template for the citation-output type "%s", does not exist',
-- Parts of the error message signalling a malformed reference.
["malformed-reference-header"] = "<span style=\"color:#dd3333\">\nError: Unable to display the reference from Wikidata properly. Technical details:\n",
["malformed-reference-footer"] = "See [[Module:wd/doc#References|the documentation]] for further details.\n</span>\n[[Category:Module:Wd reference errors]]",
["template-failure-reason"] = "* Reason for the failure of {{tl|%s}}: %s\n",
["missing-mandatory-param"] = 'The output template call would miss the mandatory parameter <code>%s</code>.',
["unknown-property-in-ref"] = 'The Wikidata reference contains the property {{property|%s}}, which is not assigned to any parameter of this template.'
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["output-types"] = {"web", "q"}, -- In this order, the output types will be tried
["param-mapping"] = {
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.lastUpdate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.subjectNamedAs] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author",
[aliasesP.authorNameString] = "author",
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages", -- extra option
[aliasesP.publishedIn] = "website",
[aliasesP.sectionVerseOrParagraph] = "at"
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.lastUpdate] = "date",
[aliasesP.retrieved] = "access-date"
}
},
["config"] = {
-- supported fields:
-- - template: name of the template used for output
-- - numbered-params: citation params accepting an arbitrary number of values by numbering the params (e.g. author1, author2)
-- - raw-value-params: params taking a raw value (which means the property is rendered with getValue with raw=true)
-- - mandatory-params: params that are required be in the template call (after potentially appending numbers to params listed in numbered-params)
-- - prioritization: table associating a list of properties, in the order in which they are preferred, to template parameters;
-- properties not mentioned here have the lowest priority;
-- prioritization of properties handled through additionalProcessedProperties is unsupported;
-- no key of this table can be from numbered-params
-- Leaving out the "template" field causes the output type to be ignored.
["web"] = {
["template"] = "Cite web",
["numbered-params"] = {"author"},
["mandatory-params"] = {"url"},
["prioritization"] = {
["date"] = {aliasesP.lastUpdate, aliasesP.publicationDate},
["title"] = {aliasesP.title, aliasesP.subjectNamedAs}
}
},
["q"] = {
["template"] = "Cite Q",
["raw-value-params"] = {"1"}, -- the first, unnamed parameter of CiteQ takes a QID, not the name of the item cited
["mandatory-params"] = {"1"},
["prioritization"] = {
["date"] = {aliasesP.lastUpdate, aliasesP.publicationDate}
}
}
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
pgkxz3kqyoyu0zj2nmtkjkdr0ntnin5
803507
799219
2026-07-30T00:16:05Z
SM7
3953
13 revisions imported from [[:en:Module:Wd/i18n]]
799219
Scribunto
text/plain
-- The values and functions in this submodule should be localized per wiki.
local p = {}
function p.init(aliasesP)
p = {
["version"] = "8", -- increment this each time the below parameters are changed to avoid reference conflict errors
["errors"] = {
["unknown-data-type"] = "Unknown or unsupported datatype '%s'.",
["missing-required-parameter"] = "No required parameters defined, needing at least one",
["extra-required-parameter"] = "Parameter '%s' must be defined as optional",
["no-function-specified"] = "You must specify a function to call", -- equal to the standard module error message
["main-called-twice"] = 'The function "main" cannot be called twice',
["no-such-function"] = 'The function "%s" does not exist', -- equal to the standard module error message
["no-such-reference-template"] = 'Error: template "%s", which is set in %s as the output template for the citation-output type "%s", does not exist',
-- Parts of the error message signalling a malformed reference.
["malformed-reference-header"] = "<span style=\"color:#dd3333\">\nError: Unable to display the reference from Wikidata properly. Technical details:\n",
["malformed-reference-footer"] = "See [[Module:wd/doc#References|the documentation]] for further details.\n</span>\n[[Category:Module:Wd reference errors]]",
["template-failure-reason"] = "* Reason for the failure of {{tl|%s}}: %s\n",
["missing-mandatory-param"] = 'The output template call would miss the mandatory parameter <code>%s</code>.',
["unknown-property-in-ref"] = 'The Wikidata reference contains the property {{property|%s}}, which is not assigned to any parameter of this template.'
},
["info"] = {
["edit-on-wikidata"] = "Edit this on Wikidata"
},
["numeric"] = {
["decimal-mark"] = ".",
["delimiter"] = ","
},
["datetime"] = {
["prefixes"] = {
["decade-period"] = ""
},
["suffixes"] = {
["decade-period"] = "s",
["millennium"] = " millennium",
["century"] = " century",
["million-years"] = " million years",
["billion-years"] = " billion years",
["year"] = " year",
["years"] = " years"
},
["julian-calendar"] = "Julian calendar", -- linked page title
["julian"] = "Julian",
["BCE"] = "BCE",
["CE"] = "CE",
["common-era"] = "Common Era" -- linked page title
},
["coord"] = {
["latitude-north"] = "N",
["latitude-south"] = "S",
["longitude-east"] = "E",
["longitude-west"] = "W",
["degrees"] = "°",
["minutes"] = "'",
["seconds"] = '"',
["separator"] = ", "
},
["values"] = {
["unknown"] = "unknown",
["none"] = "none"
},
["cite"] = {
["output-types"] = {"web", "q"}, -- In this order, the output types will be tried
["param-mapping"] = {
["web"] = {
-- <= left side: all allowed reference properties for *web page sources* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite web]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "website",
[aliasesP.referenceURL] = "url",
[aliasesP.publicationDate] = "date",
[aliasesP.lastUpdate] = "date",
[aliasesP.retrieved] = "access-date",
[aliasesP.title] = "title",
[aliasesP.subjectNamedAs] = "title",
[aliasesP.archiveURL] = "archive-url",
[aliasesP.archiveDate] = "archive-date",
[aliasesP.language] = "language",
[aliasesP.author] = "author",
[aliasesP.authorNameString] = "author",
[aliasesP.publisher] = "publisher",
[aliasesP.quote] = "quote",
[aliasesP.pages] = "pages", -- extra option
[aliasesP.publishedIn] = "website",
[aliasesP.sectionVerseOrParagraph] = "at"
},
["q"] = {
-- <= left side: all allowed reference properties for *sources other than web pages* per https://www.wikidata.org/wiki/Help:Sources
-- => right side: corresponding parameter names in (equivalent of) [[:en:Template:Cite Q]] (if non-existent, keep empty i.e. "")
[aliasesP.statedIn] = "1",
[aliasesP.pages] = "pages",
[aliasesP.column] = "at",
[aliasesP.chapter] = "chapter",
[aliasesP.sectionVerseOrParagraph] = "section",
["external-id"] = "id", -- used for any type of database property ID
[aliasesP.title] = "title",
[aliasesP.publicationDate] = "date",
[aliasesP.lastUpdate] = "date",
[aliasesP.retrieved] = "access-date"
}
},
["config"] = {
-- supported fields:
-- - template: name of the template used for output
-- - numbered-params: citation params accepting an arbitrary number of values by numbering the params (e.g. author1, author2)
-- - raw-value-params: params taking a raw value (which means the property is rendered with getValue with raw=true)
-- - mandatory-params: params that are required be in the template call (after potentially appending numbers to params listed in numbered-params)
-- - prioritization: table associating a list of properties, in the order in which they are preferred, to template parameters;
-- properties not mentioned here have the lowest priority;
-- prioritization of properties handled through additionalProcessedProperties is unsupported;
-- no key of this table can be from numbered-params
-- Leaving out the "template" field causes the output type to be ignored.
["web"] = {
["template"] = "Cite web",
["numbered-params"] = {"author"},
["mandatory-params"] = {"url"},
["prioritization"] = {
["date"] = {aliasesP.lastUpdate, aliasesP.publicationDate},
["title"] = {aliasesP.title, aliasesP.subjectNamedAs}
}
},
["q"] = {
["template"] = "Cite Q",
["raw-value-params"] = {"1"}, -- the first, unnamed parameter of CiteQ takes a QID, not the name of the item cited
["mandatory-params"] = {"1"},
["prioritization"] = {
["date"] = {aliasesP.lastUpdate, aliasesP.publicationDate}
}
}
}
}
}
p.getOrdinalSuffix = function(num)
if tostring(num):sub(-2,-2) == '1' then
return "th" -- 10th, 11th, 12th, 13th, ... 19th
end
num = tostring(num):sub(-1)
if num == '1' then
return "st"
elseif num == '2' then
return "nd"
elseif num == '3' then
return "rd"
else
return "th"
end
end
p.addDelimiters = function(n)
local left, num, right = string.match(n, "^([^%d]*%d)(%d*)(.-)$")
if left and num and right then
return left .. (num:reverse():gsub("(%d%d%d)", "%1" .. p['numeric']['delimiter']):reverse()) .. right
else
return n
end
end
return p
end
return p
pgkxz3kqyoyu0zj2nmtkjkdr0ntnin5
एमिरेट्स (हवाई सेवा)
0
47265
803345
796018
2026-07-29T16:22:21Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803345
wikitext
text/x-wiki
[[File:Emirates logo.svg|thumb|230px|alt=Logo of Emitares|एमिरेट्स एयरलाइन्स के लोगो]]
'''एमिरेट्स''' दुबई अन्तर्राष्ट्रीय विमानक्षेत्र, [[दुबई]] आधारित एगो वायुसेवा ह।<ref>GulfNews.com – [http://www.gulfnews.com/business/Aviation/10271373.html Emirates and Dnata now under ICD] {{Webarchive|url=https://archive.ph/20130103040416/http://www.gulfnews.com/business/Aviation/10271373.html |date=2013-01-03 }}</ref> इ मध्य पूर्व क प्रति सप्ताह 3600 उड़ान संचालित करे वाली सबसे बड़ वायु सेवा ह। इ आपन हब से विश्व की 6 महाद्वीपन की 81 देश की 140 शहरन में सेवा देले।<ref>http://www.emirates.com/media-centre/emirates-aircraft-cover-432-million-kilometres-across-the-globe-in-six-months</ref> इ कंपनी विश्व क दस सबसे बड़ अविराम व्यापारिक उड़ान मार्ग यानि दुबई से लॉस एंजिलस, सैनफ्रांसिस्को, डैल्लास एवं हाउस्टन के भी वायु सेवा देले।<ref>{{cite web|url=https://www.cleartrip.com/flight-booking/emirates-airlines.html|title=Emirates Airlines Flight Schedules|publisher=cleartrip.com|date=|accessdate=13 June 2017|archive-date=18 January 2017|archive-url=https://web.archive.org/web/20170118190937/https://www.cleartrip.com/flight-booking/emirates-airlines.html|url-status=dead}}</ref> एमिरेट्स, द एमिरेट्स समूह क सहभागी कंपनी ह, जेमें लगभग 50,000 कर्मचारी सेवारत बान स और इ दुबई सरकार की पूर्ण स्वामित्व में सीधा इन्वेस्ट्मेन्ट कार्पोरेशन ऑफ़ दुबई की अधीन कार्य करेले। एकर माल यातायात (कार्गो) संबंधी सब गतिविधि एमिरेट्स समूह की एमिरेट्स स्काई कार्गो प्रभाग द्वारा संचालित होला।<ref>{{cite web|url=http://www.theemiratesgroup.com/english/our-brands/air-transportation/emirates-skycargo.aspx|title=Emirates SkyCargo|publisher=The Emirates Group|accessdate=26 August 2009}}</ref>
एमिरेट्स नियमित यात्री आमद और किलोमीटर उड़ान की मामला में दुनिया क चौथा सबसे बड़ एयरलाइन ह,<ref>{{Cite web |url=http://www.iata.org/docx/WATS_2016-infographic.pdf |title=आर्काइव कॉपी के बा |access-date=2017-06-13 |archive-date=2016-07-05 |archive-url=https://web.archive.org/web/20160705112926/http://www.iata.org/docx/WATS_2016-infographic.pdf |url-status=dead }}</ref> साथे साथ अन्तर्राष्ट्रीय यात्री लोगन के सेवा प्रदान करे वाली चौथा बड़ा एयरलाइन ह।<ref>http://www.economist.com/blogs/gulliver/2015/06/worlds-largest-airlines</ref> एमिरेट्स दुबई से [[ऑकलैंड]] खातिर सबसे लंबी दुरी वाला अविराम व्यावसायिक उड़ान सेवा देवे वाला उड़ान सेवा रहे जबले की [[क़तर एयरवेज]] से इ पिछड़ नाहीं गइल।
1980 की दशक की मध्य में गल्फ एयर दुबई खातिर अपनी सेवा में कटौती करल शुरू क देले रहे। एही वजह से दुबई की शाही परिवार की सहायता से पाकिस्तान इंटरनेशनल एयरलाइन्स से भाड़ा प लिहल दुगो विमानन की साथ एमिरेट्स मार्च 1985 में आपन पहिला सेवा आरम्भ कइलस। 10 मिलियन डॉलर की आरंभिक पूंजी की साथ ए एयरलाइन्स के बिना कौनों सरकारी सहायता के आपन संचालन करे के रहे। पाकिस्तान इंटरनेशनल एयरलाइन्स अपनी एकेडमी में एमिरेट्स की केबिन क्रू के प्रशिक्षण दिहलस। इ एयरलाइन अहमद बिन सईद अल मक्तोउम, जवन की एकर वर्तमान प्रेसिडेंट हंव, उनकी नेतृत्व में शुरू कइल गइल। अपनी शुरुआत की कुछ ही वर्ष की अंदर एमिरेट्स आपन विमान दस्ता और गंतव्य की संख्या में कई गुना वृद्धि क लिहलस। अक्टूबर 2008 में एमिरेट्स आपन सब ऑपरेशन्स के दुबई इंटरनेशनल एयरपोर्ट की टर्मिनल 3 में स्थानांतरित क दिहलस।<ref>{{Cite press release|title=Emirates Announces 2009 Expansion Plan|publisher=Emirates (airline)|date=18 February 2009|url=http://www.emirates.com/english/about/news/news_detail.aspx?article=411638&offset=0|accessdate=3 August 2010}}</ref>
एमिरेट्स एयरबस और बोइंग क वाइड बॉडी वाला विमानन की मिलल-जुलल दस्ता की साथ संचालन करेले और दुनिया की कुछ सिमित एयरलाइन्स में से एगो ह जवन सिर्फ वाइड बॉडी वाला विमानन से संचालन करे न स। सितंबर 2016 में एमिरेट्स एयरबस ए-380 विमानन की सबसे बड़ दस्ता कुल 83 विमान सेवा में और 59 विमान जल्द शामिल कइल जाए वाला एयरलाइन रहे। पहिला बेर शामिल कइले की साथ ही एयरबस ए-380 भीड़-भाड़ वाला लंबा दूरी की गंतव्य खातिर एमिरेट्स की पहिला पसंद रहल बा। एमिरेट्स बोइंग विमानन क परिचालन करे वाली सबसे बड़ एयरलाइन ह।
==कॉर्पोरेट प्रबंधन==
[[File:Emirates employees at the inaugural flight to Brussels.jpg|thumb|right|Emirates cabin crew attendants]]
इ एयरलाइन एमिरेट्स ग्रुप क एगो सहायक कंपनी ह जवन स्वयं दुबई सरकार की स्वामित्व वाली निवेश कंपनी इन्वेस्टमेंट कारपोरेशन ऑफ़ दुबई क सहायक कंपनी ह।<ref>{{cite web|author=UAEinteract.com|url=http://www.uaeinteract.com/docs/Emirates_and_Dnata_now_under_ICD/33512.htm|title=Emirates and Dnata now under ICD UAE|publisher=Uaeinteract.com|date=31 दिसंबर 2008|accessdate=15 July 2009|archive-date=25 May 2009|archive-url=https://web.archive.org/web/20090525172737/http://uaeinteract.com/docs/Emirates_and_Dnata_now_under_ICD/33512.htm|url-status=dead}}</ref><ref>{{cite web|url=http://www.gulfnews.com/business/Aviation/10271373.html|title=Emirates and Dnata now under ICD|publisher=Gulfnews|accessdate=15 July 2009|archive-date=3 January 2013|archive-url=https://archive.ph/20130103040416/http://www.gulfnews.com/business/Aviation/10271373.html|url-status=dead}}</ref><ref>{{cite web|url=http://www.arabianbusiness.com/542365-dubai-moves-ownership-of-emirates-dnata-to-icd|title=Dubai moves ownership of Emirates, Dnata to ICD – Transportation|publisher=ArabianBusiness.com|date=31 दिसंबर 2008|accessdate=15 July 2009}}</ref> आपन शुरुआत की बाद से ही, दूसरा साल के छोड़ के, एमिरेट्स कबो घाटा में नाहीं रहल और एकर सालाना वृद्धि कबो 20% से कम नाहीं रहल। आपन पहिला 11 साल में इ हर साढ़े तीन साल में और ओकरी बाद से हर चार साल में दुगना आकार वाला हो जा।<ref name="CNN">[http://money.cnn.com/magazines/business2/business2_archive/2005/10/01/8359251/index.htm Rise of the Emirates Empire] {{Webarchive|url=https://web.archive.org/web/20180703221943/http://money.cnn.com/magazines/business2/business2_archive/2005/10/01/8359251/index.htm |date=2018-07-03 }} CNN Money, 1 October 2005</ref>
==स्थान==
===संधि===
एमिरेट्स विश्व की तीनगो बड़का एयरलाइन अलायंस – वन वर्ल्ड, स्काई टीम अथवा स्टार अलायन्स - में से कौनों क सदस्य नाहीं ह। साल 2000 में थोड़ा समय खातिर इ स्टार अलायन्स में शामिल होखले प विचार कइलस पर अंत में इ आपन स्वतंत्रता कायम रखलस।<ref>{{Cite news|last=Reece|first=Damian|title=Emirates poised to join Star Alliance|publisher=Telegrapg.co.uk|date=13 August 2000|url=http://www.telegraph.co.uk/finance/4461750/Emirates-poised-to-join-Star-Alliance.html|page=11|accessdate=16 October 2010|location=London}}</ref>
==संदर्भ==
<references/>
==External links==
* {{Commons category inline|Emirates (airline)}}
* {{Official website|https://www.emirates.com|mobile=mobile.emirates.com}}
* [http://www.theemiratesgroup.com/english/ Emirates Parent Company]
* [http://www.theemiratesgroup.com/english/facts-figures/annual-reports.aspx Emirates Annual Reports]
* [http://www.theemiratesgroup.com/english/news-events/news-releases/news-releases.aspx Emirates News releases]
* [http://www.sky-cargo.com Emirates SkyCargo]
[[श्रेणी:हवाई जहाज सेवा]]
343ibyz0b105h3f7zm50vx1saj9w967
औरंगाबाद रेलवे स्टेशन
0
47268
803314
733221
2026-07-29T16:20:16Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803314
wikitext
text/x-wiki
'''औरंगाबाद रेलवे स्टेशन''' सिकंदराबाद-मनमाड खंड पर स्थित एगो रेलवे स्टेशन ह। इ रेलवे स्टेशन दक्षिण मध्य रेलवे क्षेत्र की नांदेड़ प्रभाग की तहत आवेला। ए स्टेशन से [[हैदराबाद]], [[दिल्ली]], [[निजामाबाद]], [[नागपुर]], [[नाशिक]], [[नांदेड़]] और [[पुणे]] जईसन प्रमुख नगरन के साथ रेल संपर्क स्थापित कइल गइल बा।
==शासन प्रबंध==
इ भारतीय रेलवे की दक्षिण मध्य रेलवे क्षेत्र की नांदेड डिवीजन की अंतर्गत बा।
==आर्किटेक्चर==
औरंगाबाद एगो प्रसिद्ध लेकिन एगो औसत स्टेशन ह जवन महाराष्ट्र राज्य में स्थित बा। मूलतः, दक्षिण मध्य रेलवे एकर ख्याल रखेला और एके नियंत्रित करेला। इ स्टेशन एक तरफ से मुंबई से जुड़ल बा और दूसरा तरफ से हैदराबाद से जुड़ल बा। भारत की कई प्रमुख शहरन से जोड़े वाली कई गाड़ीन क सफ़र ए स्टेशन प समाप्त हो जाला, जेमें भोपाल, मुंबई, पुणे, हैदराबाद और नई दिल्ली से चले वाली गाड़ी शामिल बाड़ी स।
==कनेक्टिविटी==
औरंगाबाद क [[मुंबई]], [[दिल्ली]], [[हैदराबाद]], [[निजामाबाद]], नांदेड़, [[नागपुर]], नाशिक, [[पुणे]] आदि की साथ रेल संपर्क बा। औरंगाबाद जन शताब्दी एक्सप्रेस मुंबई से एके जोड़े वाली सबसे तेज ट्रेन ह।<ref name="station info">{{cite web|title=Aurangabad railways station|url=http://indiarailinfo.com/station/map/1669|publisher=indiarailinfo|accessdate=27 June 2014}}</ref><ref>{{Cite web|url = http://www.scr.indianrailways.gov.in/view_section.jsp?lang=0&id=0,1,291|title = Divisions in SCR|date = |accessdate = 28 June 2014|website = |publisher = Indian Railways-SCR zone portal|last = |first = }}</ref><ref>{{cite web|url=https://www.cleartrip.com/trains/stations/AWB |title=Aurangabad Train Station Time Table |publisher=cleartrip.com |date= |accessdate=14 June 2017}}</ref>
==संदर्भ==
<references/>
==बाहरी कड़ियाँ==
* [https://web.archive.org/web/20140408180437/http://www.indianrail.gov.in/ Indian Railways website]
[[श्रेणी:भारतीय रेल]]
7znr0gd8kdeetj4o8a79vo7gjk4il9a
भारतीय शास्त्रीय संगीत
0
47276
803304
753078
2026-07-29T16:07:08Z
SM7Bot
7808
/* इतिहास */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803304
wikitext
text/x-wiki
{{redirect | शास्त्रीय संगीत | यूरोप आ अमेरिका के शास्त्रीय संगीत | पच्छिमी शास्त्रीय संगीत}}
'''भारतीय शास्त्रीय संगीत''' भारतीय संगीत क अभिन्न अंग ह। शास्त्रीय संगीत के ही ‘क्लासिकल म्यूजिक’ भी कहल जाला। शास्त्रीय गायन ध्वनि-प्रधान होला, शब्द-प्रधान नाहीं। एमें महत्व ध्वनि क रहेला (एकरी चढ़ाव-उतार क, शब्द और अर्थ क नाहीं)। एके जहाँ शास्त्रीय संगीत-ध्वनि विषयक साधना क अभ्यस्त कान वाला अदमी समझ सकेला, और अनभ्यस्त कान वाला भी शब्दन क अर्थ जनले मात्र से देशी गाना चाहें लोकगीतन क सुख ले सकेला। एसे बहुत अदमी स्वाभाविक तौर पर ऊब भी जाला पर एसे ऊब जईले क वजह ओ संगीतज्ञ क कमजोरी नाहीं, लोगन में जानकारी क कमी ह।
==इतिहास==
भारतीय शास्त्रीय [[संगीत]] क परम्परा भरत मुनि की नाट्यशास्त्र और ओसे पहिले सामवेद की गायन ले जाला। भरत मुनि द्वारा रचित भरत नाट्य शास्त्र, भारतीय संगीत की इतिहास क प्रथम लिखित प्रमाण मानल जाला। {{Sfn|Rowell|2015|p=9–10, 59–61}}<ref name="Beck1995p107">{{harvnb|Beck|2012|pages=107–108}}, Quote: "The tradition of Indian classical music and dance known as ''Sangita'' is fundamentally rooted in the sonic and musical dimensions of the Vedas (Sama veda), Upanishads and the Agamas, such that Indian music has been nearly always religious in character".</ref> एकरी रचना की समय की बारे में कईगो मतभेद बा। आज की भारतीय शास्त्रीय संगीत की कईगो पहलु क उल्लेख ए प्राचीन ग्रंथ में मिलेला। भरत मुनि की नाटयशास्त्र की बाद मतङ्ग मुनि क बृहद्देशी और सारंगदेव रचित संगीत रत्नाकर, इतिहासी दृष्टि से सबसे महत्वपूर्ण ग्रंथ मनाला। बारहवीं सदी की पूर्वार्द्ध में लिखाईल सात अध्याय वाला ए ग्रंथ में संगीत व नृत्य क विस्तार से वर्णन बा।
संगीत रत्नाकर में कईगो ताल क उल्लेख बा और ए ग्रंथ से पता चलेला कि प्राचीन भारतीय पारंपरिक संगीत में ओही समय से बदलाव आवल शुरू हो गइल रहे और संगीत पहिले से उदार होखे लागल रहे मगर मूल तत्व उहे रहल। 11वीं और 12वीं शताब्दी में मुस्लिम सभ्यता क प्रसार भइले से उत्तर भारतीय संगीत की दिशा के नया आयाम मिलल। राजदरबार संगीत क प्रमुख संरक्षक बन न स और जहां अनेक शासक लोग प्राचीन भारतीय संगीत की समृद्ध परंपरा के प्रोत्साहन दिहल लो ओहीजा अपनी आवश्यकता और रुचि अनुसार एमें अनेक बदलावो कइल लो। ओही समय कुछ नया शैली भी प्रचलन में अईली स जैसे खयाल, गज़ल आदि और भारतीय संगीत क कईगो नया वाद्य यंत्र से भी परिचय भइल जैसे सरोद, सितार वगैरह।<ref>{{cite book|ref=harv|first=Emmie|last=Te Nijenhuis |title=Indian Music: History and Structure |url=https://books.google.com/books?id=NrgfAAAAIAAJ |year=1974|publisher= BRILL Academic|isbn=90-04-03978-3 }}</ref>
भारतीय संगीत क आधुनिक विद्वान् इ स्थापित क चुकल बालो कि वैदिक काल से शुरू भइल भारतीय वाद्यन क यात्रा क्रमश: एक की बाद दूसरा विशेषता जोड़ के ए यंत्रन के सँवारत गइल। एक-तंत्री वीणा ही त्रितंत्री बनल और सारिका युक्त होके मध्य-काल की पूर्व में किन्नरी वीणा की नाम से प्रसिद्ध भइल। मध्यकाल में इ यंत्र जंत्र कहलाए लागल जवन की बंगाल की कारीगरन द्वारा आज भी एही नांव से पुकारल जाला। भारत में पहुँचल मुस्लिम संगीतकार तीन तार वाली वीणा के सह (तीन) + तार = सहतार या सितार कहे लागल लो। एही प्रकार से सप्त तंत्री अथवा चित्रा-वीणा, सरोद कहलाए लागल। उत्तर भारत में मुगल राज्य ज्यादा फैलल रहे जेकरी कारण उत्तर भारतीय संगीत प मुस्लिम संस्कृति व इस्लाम क प्रभाव ज्यादा महसूस कइल गइल। जबकि दक्षिण भारत में प्रचलित संगीत कौनों प्रकार की मुस्लिम प्रभाव से अछूता रहल।
बाद में सूफी आंदोलन भी भारतीय संगीत पर आपन प्रभाव जमवलस। आगे चलके देश की विभिन्न हिस्सन में कईगो नई पद्धतिन व घराना क जन्म भइल। ब्रिटिश शासनकाल की दौरान कईगो नया वाद्य प्रचलन में अईन स, पाश्चात्य संगीत से भी भारतीय संगीत क परिचय भइल।
==संदर्भ==
{{reflist|33em}}
[[श्रेणी:संगीत]]
[[श्रेणी:भारतीय संस्कृति]]
4gqahipos85ddcgmanh8os6uwus4e62
कुमारकोम लेक रिजॉर्ट
0
47448
803346
763577
2026-07-29T16:22:25Z
SM7Bot
7808
/* स्थान */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803346
wikitext
text/x-wiki
'''कुमारकोम लेक रिजॉर्ट''' कुमारकोम नॉर्थ पोस्ट की शांत माहौल में आइल बा।<ref>{{cite web|url=http://www.kumarakomlakeresort.in/about_kumarakom_lake_resort.html|title=Official Website|publisher=kumarakomlakeresort.in|accessdate=19 June 2017|archive-date=13 June 2017|archive-url=https://web.archive.org/web/20170613194149/http://www.kumarakomlakeresort.in/about_kumarakom_lake_resort.html|url-status=dead}}</ref><ref>{{cite web|url=http://www.vervemagazine.in/travel-and-spaces/dreamy-quietude-kumarakom-lake-resort-kerala|title=The Dreamy Quietude Of Kumarakom Lake Resort|publisher=vervemagazine.in|date=7 March 2017}}</ref>
==स्थान==
इ रिसोर्ट कोट्टयम रेलवे स्टेशन से लगभग 15 किमी दूर और [[कोचीन]] अंतर्राष्ट्रीय हवाई अड्डा से 72 किमी दूर, नेडुम्बसेरी में बा। इ रिसॉर्ट एइसन जगह प बा जहाँ प्रकृति एकर चुंबन ले ले और एकरी अलावा, वेंबनाद झील, केरल बॅकवाटर और कुमारकोम पक्षी अभयारण्य जैसन स्थान एकरी नजदीक आइल बान स। कुमारकोम लेक रिज़ॉर्ट कईगो पिकनिक स्थल जैसे वाइकम बीच, पैतिरमानल द्वीप और अरुविककुशी झरना जैसन स्थान प आसान पहुँच प्रदान करेला।<ref>{{cite web|url=https://www.cleartrip.com/hotels/info/kumarakom-lake-resort-40847|title=About Kumarakom Lake Resort|publisher=cleartrip.com|accessdate=19 June 2017}}</ref>
कुमारकोम लेक रिजॉर्ट चार साल ले [[भारत]] की अग्रणी रिसॉर्ट के रूप में प्रशंसित वर्ल्ड ट्रैवल अवार्ड क विजेता रही चुकल बा। एकरी आलावा एके समय समय प निम्नलिखित पुरस्कारन आ सम्मान से नवाजल गइल बा:<ref>{{cite web|url=http://prestigiousstarawards.com/qualified-venues/kumarakom-lake-resort/|title=Kumarakom Lake Resort|publisher=prestigiousstarawards.com|accessdate=19 June 2017}}</ref>
* केरल 2014 की राज्य पर्यटन पुरस्कार में केरल क सर्वश्रेष्ठ पांच सितारा होटल की रूप में सम्मानित कइल गइल
* भारत की प्रमुख रिसॉर्ट के रूप में विश्व यात्रा पुरस्कार 06-07, 07-08, 09-10 और 11-12 क विजेता।
* एशियाई वास्तुकला उत्कृष्टता पुरस्कार खातिर नामांकित
* यात्रा और अवकाश द्वारा एशिया क शीर्ष 50 रिसॉर्ट्स में से एगो।
* कोंडे नेस्ट ट्रेवलर्स रीडर्स पोल में शीर्ष 100 रिसॉर्ट्स में से एगो।
* वाल स्ट्रीट जर्नल द्वारा प्रशंसित 'प्लेस फॉर अ हॉलिडे पेम्पर'
* भारत में शीर्ष पांचगो लक्जरी होटल में से एगो: अतुल्य भारत, सितंबर-अक्टूबर 2004।
* टीटीएफ बैंगलोर में अनोखा अवकाश संपत्ति खातिर पुरस्कार जीतलस
* 'बेस्ट न्यू प्रोडक्ट' की रूप में SATTE पुरस्कार विजेता 06 - 07
==संदर्भ==
[[श्रेणी:होटल]]
6emp8elylx9rczpmap2c41k6o2sea2d
कोणार्क एक्सप्रेस
0
47926
803349
763583
2026-07-29T16:22:39Z
SM7Bot
7808
/* कोच संरचना */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803349
wikitext
text/x-wiki
{{Infobox rail service
|name = कोणार्क एक्सप्रेस
| image= 11020 Konark Express (2).jpg
| logo= Konark express 11020.jpg
| caption =
| type = एक्सप्रेस ट्रेन
| locale = [[ओडिशा]], [[आंध्र प्रदेश]], [[तेलंगाना]], [[कर्नाटक]] आ [[महाराष्ट्र]]
| first = 16 अगस्त 1978
| last =
| operator = मध्य रेलवे
| ridership =
| start = [[भुबनेश्वर]]
| stops = 35
| end = [[मुंबई]]
| distance = 1932 किलोमीटर
| journeytime = 37 घंटा, 15 मिनट
| frequency = रोज
| class = AC 2 टायर, AC 3 टायर, स्लीपर, जनरल
| seating = हँऽ
| sleeping = हँऽ
| autorack =
| catering = हँऽ, पेमेंट पर
| observation = छोट ICF खिड़की
| entertainment = None
| baggage = सीट के नीचे
| otherfacilities=
| stock =
| gauge = {{RailGauge|1676mm}}
| el =
| trainnumber = 11019 / 11020
| speed = औसतन फी घंटा 51 किलोमीटर
| map = [[File:Konark express route.png|250px]]
| map_state =
}}
'''कोनार्क एक्सप्रेस''', 1978 में मधु दंडवते द्वारा शुरू कइल गइल श्रेणीरहित कई गो सुपरफास्ट ट्रेन में से एगो रहल। ई गाड़ी [[सिकंदराबाद]] आ [[भुवनेश्वर]] की बीच में दैनिक सेवा दे। इ 2119/2120 ट्रेन की साथ चले और 2101/2102 मिनार सुपरफास्ट एक्सप्रेस जवन की सिकंदराबाद और बॉम्बे वीटी (अब सीएसटीएम) की बीच चले, ओकरी साथ आपन रेक साझा करे। ए रेल क शुरूआत में नेवी ब्लू बाहरी रंग सज्जा रहल, और एकर रेक साझाकरण की व्यवस्था की कारण एकरी खिड़कियन प एगो सफेद पट्टी रहे।
बॉम्बे आ [[भुवनेश्वर]] की बीच्चे एगो विशेष रेलगाड़ी की मांग में वृद्धि की साथ, ए ट्रेन के बॉम्बे वी.टी. ले सन 1994 में बढ़ा दिहल गइल रहे जेसे 2101/2102 मिनार एक्सप्रेस (जवने क बाद में 2701/02 हुसैनसागर एक्स्प्रेस जगह लिहलस) की साथ रेक साझाकरण क व्यवस्था समाप्त हो गइल। विस्तार के बाद ए ट्रेन के 1994 में 1019/1020 नया नंबर दिहल गइल। इ देश की पूर्वी और पश्चिमी राज्यन के जोड़े वाली सबसे पुरान कईगो ट्रेन में से एगो ह। वर्तमान में इ 11019/11020 के रूप में कार्य करतीया और इ मुंबई छत्रपति शिवाजी टर्मिनस (सीएसटीएम) के भुवनेश्वर से जोड़ेले।
==रास्ता==
दादर, कल्याण, लोनावाला, [[पुणे]], दौंड, [[सोलापुर]], [[गुलबर्गा]], तंदूर, [[सिकंदराबाद]], काजीपेट, वारंगल, महबूबबाद, विजयवाड़ा, एलुरु, राजमुंड्री, विशाखापट्टनम, श्रीकाकुलम, पलासा और बेरहमपुर और खुर्दा रोड ए गाड़ी की रास्ता में प्रमुख हाल्ट हंव स।<ref>http://indiarailinfo.com/train/timetable/konark-express-11020-bbs-to-cstm/946/238/1620</ref><ref>{{cite web| url=https://www.cleartrip.com/trains/11020| publisher=cleartrip.com| title=Konark Express train time table| accessdate=7 July 2017| archive-date=5 April 2016| archive-url=https://web.archive.org/web/20160405232315/http://www.cleartrip.com/trains/11020| url-status=dead}}</ref>
एकर यात्रा क कुल दूरी 1932 किमी ह एमें पूर्व [[आंध्र प्रदेश]] (वर्तमान तेलंगाना और आंध्र प्रदेश) राज्य में लगभग 1066 किलोमीटर क दूरी शामिल बा।
==कोच संरचना==
एमें 1गो एसी 2 टीयर, 5गो एसी 3 टीयर, 12गो स्लीपर, 1गो पैंट्री कार, 2गो अनारक्षित और 2गो ब्रेक-कम-गार्ड वैन हो न स, एमें कुल मिला के 23गो कोच होन स। और एकर रख रखाव ईस्ट कोस्ट रेलवे और मध्य रेलवे क्षेत्र द्वारा कइल जाला।<ref>{{cite web| url=https://erailway.co.in/trains/11020-KONARK-EXPRESS| publisher=erailway.co.in| title=Konark Express Coach Position| accessdate=7 July 2017}}{{Dead link|date=October 2023 |bot=InternetArchiveBot |fix-attempted=yes }}</ref>
==रेल इंजन==
एकरी रास्ता में खाली कुछ भाग क विद्युतीकरण होखले की कारण, एके दुगो लोकोमोटिव दिहल गइल बा। [[मुंबई]] सीएसटी और विजयवाड़ा जंक्शन की बीच्चे, एमें कल्याण शेड क डब्लूपीपी-4 डी लागेला और विजयवाडा जंक्शन से भुवनेश्वर रेलवे स्टेशन ले लालगुडा शेड क डब्लूएपी-4 लागेला।
==घटना सभ==
31 जुलाई 2008 के कोनार्क एक्सप्रेस क इंजन और दुगो कोच गुलबर्गा की पास गंगापुर स्टेशन पर पटरी से उतर गईन स जेमें दू लोग घायल हो गइल। पटरी से उतरले क कारण अबले मालूम नाहीं चलल बा।<ref>{{cite web|title=Konark Express derails, two people injured|url=http://news.oneindia.in/2008/07/31/konark-express-derails-two-injured-1217496995.html|publisher=OneIndia News|accessdate=25 July 2011|archive-date=9 October 2012|archive-url=https://web.archive.org/web/20121009155026/http://news.oneindia.in/2008/07/31/konark-express-derails-two-injured-1217496995.html|url-status=dead}}</ref>
===फोटो गैलरी===
<gallery>
File:Konarkexpress.JPG|Konark express(11020) at Kazipet
File:WDP4D 05112016.jpg|thumb|A WDP4D hauling Bhubaneshwar-bound Konark Express near Secunderabad
File:Konark Exp at Moulaali1.jpg|Konark Exp at Moulaali
File:11020 Konark Express.jpg|11020 Konark Express
</gallery>
==संदर्भ==
<references/>
[[श्रेणी:भारतीय रेल के गाड़ी]]
kk9wr0mmw0k2aksjrqflqvgh5ozk3t9
जगजीत सिंह
0
47981
803294
801919
2026-07-29T16:06:24Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803294
wikitext
text/x-wiki
{{Infobox person
| name = जगजीत सिंह
| image = Jagjit Singh (Ghazal Maestro).jpg
| caption = साल 2003 में भुबनेश्वर में एगो कार्यक्रम के दौरान जगजीत सिंह
| birth_name = जगमोहन सिंह धीमन
| birth_date = {{Birth date|df=yes|1941|2|8}}
| birth_place = श्री गंगानगर, बीकानेर राज, राजपुताना एजेंसी, ब्रिटिश भारत
| death_date = {{Death date and age|df=yes|2011|10|10|1941|2|8}}
| death_place = मुंबई, महाराष्ट्र
| occupation = गायक, संगीत निर्देशक
| spouse = {{marriage|चित्रा सिंह|1969}}
| children = विवेक
| module=
{{Infobox musical artist | embed=yes
| background = solo_singer
| genre = ग़ज़ल, शास्त्रीय, भक्ती, लोक संगीत
| instrument = गला, हारमोनियम, तानपूरा, पियानो
| years_active = 1961–2011
| label = ईएमआइ, एचएमवी, सारेगामा, यूनिवर्सल, सोनी बीएमजी, सीबीएस, पौलीडोर, टिप्स, वीनस, टी-सीरीज, मैग्ना साउंड, बिग, टाइम्स
| website =
| associated_acts=
}}
| alias = गजल सम्राट
}}
'''जगजीत सिंह''' (8 फ़रवरी 1941 - 10 अक्टूबर 2011) क नांव बेहद लोकप्रिय ग़ज़ल गायकन में शुमार कइल जाला। उन क संगीत अंत्यंत मधुर रहल और उन क आवाज़ संगीत की साथ खूबसूरती से घुल-मिल जाला। खालिस उर्दू जाने वालन क मिल्कियत समझल जाए वाली, नवाबन की दुनिया में झनके वाला और शायरन की महफ़िलन में वाह-वाह की दाद प इतराए वाली ग़ज़लन के आम आदमी तक पहुंचवले क श्रेय अगर केहू के पहिले पहल देवे के होखे त जगजीत सिंह क ही नांव ज़बान प आवेला। उन क ग़ज़ल न सिर्फ़ उर्दू की कम जानकारन की बीच शेरो-शायरी की समझ में इज़ाफ़ा कइली स बल्कि ग़ालिब, मीर, मजाज़, जोश और फ़िराक़ जैसन शायरन से भी उन क परिचय करवली स।
जगजीत सिंह के सन 2003 में भारत सरकार द्वारा कला की क्षेत्र में पद्म भूषण से सम्मानित कइल गइल। फरवरी 2014 में इनकी सम्मान व स्मृति में दुगो डाक टिकट भी जारी कइल गइल।<ref>{{cite web|title=PM Manmohan Singh releases Stamp to Honor Jagjit Singh|url=http://news.biharprabha.com/2014/02/pm-manmohan-singh-releases-stamp-to-honor-jagjit-singh/|work=IANS|publisher=news.biharprabha.com|accessdate=8 February 2014|archive-date=14 November 2014|archive-url=https://web.archive.org/web/20141114111946/http://news.biharprabha.com/2014/02/pm-manmohan-singh-releases-stamp-to-honor-jagjit-singh/|url-status=dead}}</ref>
==आरंभिक दिन==
[[File:Shahid Kabir with Jagjit Singh and his son, Sameer Kabeer.jpg|thumb|Jagjit Singh (middle) with poet Shahid Kabir and his son, Sameer Kabeer]]
जगजीत जी क जन्म 8 फरवरी 1941 (मृत्यु 10 ऑक्टोबर 2011) के [[राजस्थान]] की गंगानगर में भइल रहे। पिता सरदार अमर सिंह धमानी भारत सरकार क कर्मचारी रह न। जगजीत जी क परिवार मूलतः [[पंजाब]] (भारत) की रोपड़ ज़िला की दल्ला गांव क रहे वाला ह।<ref>http://www.firstpost.com/living/the-guilty-pleasure-of-jagjit-singh-indias-ghazal-king-103483.html</ref> मां बच्चन कौर पंजाब क ही समरल्ला की उट्टालन गांव क रहे वाला रहली। <ref name=TOI20021110>{{cite news |last=Sawhney |first=Anubha |title=Unforgettable moments with Jagjit Singh|url=http://timesofindia.indiatimes.com/articleshow/27885263.cms|accessdate=11 January 2012|newspaper=Times of India|date=10 November 2002}}</ref> जगजीत क बचपन का नांव जीत रहे। करोड़ों सुने वाला श्रोता लोगन की चलते सिंह साहब कुछ ही दशक में जग के जीते वाला जगजीत बन गई न। शुरूआती शिक्षा गंगानगर की खालसा स्कूल में भइल और बाद में पढ़े खातिर जालंधर आ गई न। डीएवी कॉलेज से स्नातक क डिग्री लीह न और एकरी बाद कुरूक्षेत्र विश्वविद्यालय से इतिहास में पोस्ट ग्रेजुएशन कई न।<ref name=Guardian20111025>{{cite news |newspaper=The Guardian |location=London |title=Jagjit Singh obituary |first=Asjad |last=Nazir |date=25 October 2011 |url=https://www.theguardian.com/music/2011/oct/25/jagjit-singh-obituary |accessdate=11 January 2012}}</ref>
इन क पढ़ाई में दिलचस्पी कम रहे। कई क्लास मे त दू- दू साल गुज़रल। एक बेर आपन चचेरी बोहीन की शादी में जमल महिला मंडली की बैठक में जाके गीत गावे लग न। पूछले प कहें कि सिंगर नहीं बनती त धोबी होतीं। पिता के इजाज़त की बग़ैर फ़िल्म देखल और टाकीज में गेट कीपर के घूस देके हॉल में घुसल आदत रहे।
==संगीत क सफ़र==
बचपन में अपनी पिताजी से संगीत विरासत में मिलल रहे। गंगानगर में ही पंडित छगन लाल शर्मा की सानिध्य में दू साल ले शास्त्रीय संगीत सीखले क शुरूआत कई न। आगे जाकर सैनिया घराना की उस्ताद जमाल ख़ान साहब से ख्याल, ठुमरी और ध्रुपद क बारीकी सीख न। पिता क ख़्वाहिश रहे कि उन क बेटा भारतीय प्रशासनिक सेवा (आईएएस) में जाए लेकिन जगजीत जी प त गायक बनले क धुन सवार रहे। कुरुक्षेत्र विश्वविद्यालय में पढ़ाई की दौरान संगीत में उन क दिलचस्पी देख के कुलपति प्रोफ़ेसर सूरजभान जगजीत सिंह जी के बहुत उत्साहित कई न। उनहीं की कहले प उ 1965 में [[मुंबई]] आ गई न।<ref name=Guardian20111025 /> एही जा से संघर्ष क दौर शुरू भइल। उ पेइंग गेस्ट की तौर पर रहें और विज्ञापनन खातिर जिंगल्स गाके या शादी-समारोह वगैरह में गाके रोज़ी रोटी का जुगाड़ करें। 1967 में जगजीत जी क मुलाक़ात चित्रा जी से भइल। दू साल बाद दुनुं 1969 में परिणय सूत्र में बंध गइल लो।.<ref name="TOI20021110"/>
{{clear}}
==संदर्भ==
{{reflist|33em}}
==बाहरी कड़ी==
{{Commons category|Jagjit Singh}}
* [http://www.bbc.co.uk/music/artists/ff7d390f-03be-40e4-9210-3e0f660966df Jagjit Singh on BBC]
[[श्रेणी:सिनेमा]]
[[श्रेणी:भारतीय गायक]]
ff849q9g4xns6xkkc9ayr1n61r1legq
एयर चाइना
0
48301
803352
773184
2026-07-29T16:22:55Z
SM7Bot
7808
/* इतिहास */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803352
wikitext
text/x-wiki
'''''एयर चाइना लिमिटेड''''', [[चीन|पीपुल्स रिपब्लिक ऑफ चाइना]] क ध्वजवाहक और एकर कईगो प्रमुख एयरलाइंस में से एगो ह, जेकर मुख्यालय [[बीजिंग]] में शुनाय जिला ह। एयर चाइना क उड़ान संचालन केंद्र बीजिंग कैपिटल इंटरनेशनल एयरपोर्ट पर आइल बा।<ref>{{cite web|title=एयर चाइना के विषय में |url=http://www.staralliance.com/en/about/airlines/air-china_airlines/|publisher=staralliance.com|accessdate=24 July 2017}}</ref> सन 2015 में, इ एयरलाइन 90 मिलियन घरेलू और अंतरराष्ट्रीय यात्रिन के 80% के औसत लोड फैक्टर के साथ गंतव्य प पहुँचवलस।<ref>{{cite web|title=एयर चाइना वार्षिक रिपोर्ट 2015|url=http://www.airchina.com.cn/en/images/investor_relations/2016/03/31/E993685CDB67732B19075FBAF25FAD0D.pdf|publisher=airchina.com.cn|accessdate=24 July 2017}}</ref>
==इतिहास==
[[File:AirChinaBuildingShunyi1.JPG|thumb|एयर चाइना HQ Building]]
एयर चाइना क स्थापना और संचालन क शुरुआत 1 जुलाई 1988 के 1987 में चीनी सरकार की एगो फैसला की परिणामस्वरूप भइल रहे जवने की अनुसार चीन क नागरिक विमानन प्रशासन (सीएएसी) की सब ऑपरेटिंग डिवीजन क निम्नलिखित छौगो अलग-अलग एयरलाइंस में विभाजन करा गइल रहे: एयर चाइना, चाइना ईस्टर्न, चाइना साउथर्न, चाइना नॉर्थन, चाइना साउथ वेस्ट, और चाइना नॉर्थवेस्ट। एयर चाइना के इंटरकॉन्टिनेंटल फ्लाइट्स क मुख्य जिम्मेदारी दिहल गइल और सीएएसी क लंबी दूरी वाला विमान (बोइंग 747 एस, 767 एस और 707 एस) और दूर क मार्गन के अधिग्रहित क लिहलस।<ref>{{cite web|title=एयर चाइना 15 नए विमान खरीदने की योजना बना रही है|url=http://www.seattlepi.com/business/article/Air-China-plans-to-buy-15-new-planes-1099632.php|publisher=seattlepi.com|accessdate=24 July 2017}}</ref><ref>{{cite web|title=एयर चाइना एयरलाइंस|url=https://www.cleartrip.com/flight-booking/air-china-airlines.html|publisher=cleartrip.com|accessdate=24 July 2017}}</ref>
जनवरी 2001 में, पूर्व सीएएसी क दसगो एयरलाइंस विलय योजना खातिर सहमत भईन स, जेकरी अनुसार एयर चाइना के चीन साउथवेस्ट एयरलाइंस क अधिग्रहण करे के रहे। अधिग्रहण से पहिले, एयर चाइना देश क चौथा सबसे बड़ा घरेलू एयरलाइन रहे और ए विलय से 118 विमानन क एगो बेड़ा की साथ 56 अरब युआन (USD $ 8.63 बिलियन) क संपत्ति क एगो समूह बनल। अक्टूबर 2002 में, एयर चाइना चीन की राष्ट्रीय विमानन होल्डिंग और चीन साउथवेस्ट एयरलाइंस की साथ समेकित हो गइल।
15 दिसंबर 2004 के, एयर चाइना के [[हांगकांग]] और लंदन स्टॉक एक्सचेंज प सफलतापूर्वक सूचीबद्ध कइल गइल। 2006 में, एयर चाइना स्टार अलायंस में शामिल होखे खातिर एगो समझौता प हस्ताक्षर कइलस और इ 12 दिसंबर, 2007 के शंघाई एयरलाइंस की साथ गठबंधन क सदस्य बन गइल।
जुलाई 2009 में, एयर चाइना अपनी परेशानी से घिरल सहायक एयर मकाऊ से 19.3 मिलियन अंश क अधिग्रहण कइलस, जेसे ए कैरियर में एकर हिस्सेदारी 51% से बढ़कर 80.9% हो गइल। एक महीना बाद, एयर चाइना हांगकांग में आपन उपस्थिति क विस्तार कइले की इरादा से, कैथै प्रशांत में आपन हिस्सेदारी 17.5% से बढ़ाकर 30% तक करे खातिर 6.3 अरब डॉलर (यूएस $ 813 मिलियन) खर्च कइलस।
अप्रैल 2010 में, एयर चाइना शेन्ज़ेन एयरलाइंस में शेयरहोल्डिंग क वृद्धि पूरा कके शेन्ज़ेन एयरलाइंस क कंट्रोलिंग शेयरधारक बन गइल और एकरी चलते एयर चाइना बीजिंग, चेंगदू, और शंघाई में आपन स्थिति मजबूत कइलस और आपन घरेलू नेटवर्क में और संतुलन हासिल कइलस।<ref>{{cite web|title=एयर चाइना शीर्ष स्पेनिश पर्यटन पुरस्कार प्राप्त करता है |url=http://www.eturbonews.com/19875/air-china-receives-top-spanish-tourism-award|publisher=eturbonews.com|accessdate=24 July 2017}}</ref>
2 दिसंबर 2010 के, एयर चाइना स्पेन क सर्वोच्च पर्यटन उद्योग पुरस्कार प्राप्त कइलस, और इ पुरस्कार प्राप्त करे वाला एयर चाइना पहिला विदेशी एयरलाइन बनल, आमतौर प इ पुरस्कार खाली एइसन संगठन और व्यक्ति लोगन के दिहल जा जवन की स्पेनिश पर्यटन उद्योग में कौनों तरह क योगदान दें स।
23 दिसंबर 2010 के, एयर चाइना ओ संयुक्त टिकटन क पेशकश करे वाली पहिला चीनी एयरलाइन बन गइल, जेमें पास की शहरन में घरेलू उड़ान और शटल बस सेवा शामिल रहे।<ref>{{cite web|title=एयर चाइना घरेलू मार्गों के लिए उड़ान शटल बस संयुक्त टिकट की शुरूआत करती है|url=http://www.smartinternchina.com/air-china-press-release|publisher=smartinternchina.com|accessdate=24 July 2017|archive-date=4 April 2016|archive-url=https://web.archive.org/web/20160404171143/http://www.smartinternchina.com/air-china-press-release|url-status=dead}}</ref>
एयर चाइना 15 नवंबर, 2011 के अपनी विमानन में वाई-फाई इंटरनेट सेवा दिहल शुरू क दिहलस, जेसे इ ए तरह क सेवा क पेशकश करे वाली पहिला चीनी वाहक बन गइल।
==इहो देखल जाय==
* [[एयर इंडिया]]
==संदर्भ==
{{reflist}}
[[श्रेणी:हवाई जहाज सेवा]]
q5da004yk7d16nptlxpuhw33hhbxvhf
बोगमालो बीच रिसोर्ट
0
48366
803339
782531
2026-07-29T16:21:57Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803339
wikitext
text/x-wiki
'''बोगमालो बीच रिसोर्ट''', [[गोवा]] की दक्षिणी छोर पर, बोगमालो बीच पर, स्थित एगो पाँच-सितारा होटल ह। प्राकृतिक रूप से बनल ई बीच, अर्धचंद्र आकार क बा आ पश्चिमी तटीय मैदान क सबसे व्यस्तम समुद्र तट में से एगो ह। इ रिसोर्ट किनारे पर चले वाला कसीनो, जेकर संचालन ट्रेड विंग्स होटल लिमिटेड की निदेशक द्वारा होला, नॉटिलस रेस्तरां, 126 डीलक्स और सेमी-डीलक्स कमरा और एकरी पीछे मौजूद एगो स्विमिंग पूल की साथ गर्व से खड़ा बा।<ref>{{cite web|title=official website|url=http://www.bogmallobeachresort.com/aboutus.php|publisher=bogmallobeachresort.com}}{{Dead link|date=March 2025 |bot=InternetArchiveBot |fix-attempted=yes }}</ref>
मुख्य इमारत द्वारा घेरल क्षेत्र लगभग 1,332.14 वर्ग मीटर (14,338.98 फीट<sup>2</sup>) बा आ इमारत की सामने क भाग 518.24 वर्ग मीटर (5,578.2 9 फीट<sup>2</sup>) में बनावल गइल बा। दक्षिण में पूल 194.57 वर्ग मीटर क्षेत्र (2,094.31 फीट<sup>2</sup>) क क्षेत्र घेरेला।
==इतिहास==
एकर स्वामित्व ओबेराय समूह की लगे बा और एके पहिले होटल ओबेराय के नांव से जानल जा, जेकर बाद में बोगमालो बीच की ऊपर से पुनः नामकरण कइल गइल। द लिंक बैंड क सह-संस्थापक झिस्टो पेस याद करत बतावे न की प्रसिद्ध पॉप गायक और गिटारवादक स्वर्गीय टेरेंस वाज़, जवन की बैंड 'लिंक्स' क सदस्य रह न, ऊ ओबेराय रिसोर्ट में नियमित रूप से गिटार बजावें, ऊ वर्ष 1974 में ए बैंड से जुड़ न और 1994 तक बजवन।<ref>{{cite web|title=बोगमालो बीच रिसोर्ट|url=https://www.cleartrip.com/hotels/info/bogmallo-beach-resort-41661|publisher=cleartrip.com}}</ref>
==मीडिया==
27 अप्रैल 2015 के वास्को पुलिस विभाग तटवर्ती कैसीनो के सील क दिहलस काहें की ए रिसॉर्ट क 5 सितारा लाइसेंस समाप्त हो गइल रहे और इ कैसीनो के गैर-कानूनी रूप से संचालित करत रहे।
इ रिसोर्ट नलकॉन 2015 क मेजबानी कइलस, जवन की 4 फरवरी और 5 फरवरी के [[एंड्रॉइड]] सुरक्षा खामी और ओकर फायदा उठवले में रुचि रखे वाला लोगन खातिर 2 दिन अनंत श्रीवास्तव और आदित्य गुप्ता द्वारा आयोजित कइल गइल रहे।
==संदर्भ==
{{reflist}}
[[श्रेणी:होटल]]
[[श्रेणी:गोवा के होटल]]
72bs41r2ywlrfo1zjmx7fax2eg8f5ia
तिरुपति रेलवे स्टेशन
0
48916
803342
762640
2026-07-29T16:22:09Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803342
wikitext
text/x-wiki
{{Infobox station
| name = तिरुपति
| native_name =
| native_name_lang= te
| type = एक्सप्रेस ट्रेन, यात्री ट्रेन तथा कम्यूटर रेल स्टेशन
| style = Indian railway
| image = TPTY Name Board.jpg
| image_caption = तिरुपति रेलवे स्टेशन
| address = चेन्नई-अनंतपुर उच्च मार्ग 205, तिरुपति,आंध्र प्रदेश
| country = भारत
| coordinates = {{Coord|13.6279|N|79.4194|E|type:railwaystation_region:IN|format=dms|display=inline,title}}
| elevation = {{convert|150|m|ft|0|abbr=on}}
| line = गुडुर-काटपाडी शाखा लाइन
| other =
| structure = Standard (on ground station)
| platform = 5
| tracks = 8
| parking = Available
| closed =
| rebuilt =
| electrified = Yes
| ADA =
| code = Indian railway code
| owned =
| operator =
| status = Functioning
| former =
| passengers =
| pass_year =
| pass_percent =
| pass_system =
}}
'''तिरुपति रेलवे स्टेशन''' (स्टेशन कोड: TPTY) भारत की आंध्र प्रदेश राज्य में स्थित रेलवे स्टेशन ह, इ तिरुपति शहर और बड़ी संख्या में चित्तूर जिला की तिरुमला वेंकटेश्वर मंदिर की दर्शन खातिर आवे वाला यात्रीन की नियमित प्रवाह के सेवा देला।<ref>{{cite web|title=Station Code Index|url=http://www.indianrailways.gov.in/railwayboard/uploads/directorate/coaching/TAG_2015-16/Station_Code(1).pdf|publisher=Portal of Indian Railways|accessdate=14 Aug 2017|page=2|format=PDF}}</ref>
==इतिहास==
दक्षिण भारतीय रेलवे में एगो मीटर गेज लाइन, दक्षिण आरकॉट जिला में विल्लुपुरम से 1891 में शुरू भइल और काटपाड़ी और चित्तूर से होक पकाला तक गइल। काटपाडी-गुडुर लाइन, जवन तिरुपति के कवर करेले, अब इ ब्रॉड गेज करा गइल बा।<ref>{{cite web|title=Katpadi Jn. - Pakala Jn|url=http://www.irfca.org/articles/metric/kpd.html|publisher=irfca.or|accessdate=14 Aug 2017}}</ref>
==वर्गीकरण==
तिरुपति के गुंटकाल रेलवे डिवीजन में ए 1 श्रेणी की स्टेशन की रूप में वर्गीकृत कइल गइल रहे। इ भारतीय रेलवे की शीर्ष सौ बुकिंग स्टेशन में से एगो ह।<ref>{{cite web|title=Indian Railways Passenger Reservation Enquiry|url=http://www.indianrail.gov.in/7days_Avl.html|publisher=IRFCA.|accessdate=14 Aug 2017|archive-date=10 May 2014|archive-url=https://web.archive.org/web/20140510115649/http://www.indianrail.gov.in/7days_Avl.html|url-status=bot: unknown}}</ref><ref>{{cite web|title=Category of Stations over Guntakal Division|url=http://www.scr.indianrailways.gov.in/view_section.jsp?lang=0&id=0,1,291,353,1280,1349|publisher=South Central Railway zone. Portal of Indian Railways|accessdate=14 Aug 2017}}</ref>
==राजस्व==
नीचे दिहल गइल तालिका पिछला सालन क स्टेशन क यात्री आय दर्शावतीया।<ref>{{cite web|title=Tirupati Train Station|url=https://www.cleartrip.com/trains/stations/TPTY|publisher=cleartrip.com|accessdate=14 Aug 2017}}</ref>
{| class="wikitable" border="1" cellpadding="6" style="border-collapse: collapse;text-align:center"
|+
!साल
!राजस्व (लाख में)
|-
| 2011–12
| 15536.77
|-
|2012–13
|16973.58
|-
|2013–14
|19787.68
|-
|2014–15
|23226.40
|}
==उपलब्ध सुविधा==
तिरुपति रेलवे स्टेशन प 5गो प्लेटफार्म बान स और एमें से हरेक 24 से अधिक कोच वाला ट्रेन के संभाल सकेला। प्रत्येक प्लेटफार्म पर एस्केलेटर उपलब्ध बान स। तिरुपति रेलवे स्टेशन प्रति दिन 45 जोड़ा से अधिक पैसेंजर ट्रेन के संभालेला।
==सन्दर्भ==
{{reflist}}
[[श्रेणी:भारतीय रेल]]
259lycndnxyqfrxs7lkr6iev0aqpud8
सिंगापुर एयरलाइंस
0
48932
803295
773182
2026-07-29T16:06:31Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803295
wikitext
text/x-wiki
{{Infobox airline
|airline=सिंगापुर एयरलाइंस
|logo=
|logo_size=200
|fleet_size=सिंगापुर एयरलाइंस बेड़ा|111
|destinations=सिंगापुर एयरलाइंस स्थल|64
|IATA=SQ
|ICAO=SIA
|callsign=सिंगापुर
|company_slogan=A Great Way to Fly
|founded={{Start date and years ago|1947|05|01|df=y}} (as Malaysia–सिंगापुर एयरलाइंस)
|commenced= {{nowrap|{{Start date and years ago|1972|10|01|df=y}}}}
|headquarters=Airline House<br />25 Airline Road<br />Singapore 819829<br />सिंगापुर
|parent=Temasek Holdings (56%)
|key_people=
<div>
* Peter Seah Lim Huat <br> {{small|(Chairman)}}
* Goh Choon Phong <br> {{small|(Chief Executive Officer)}}
|hubs=सिंगापुर चंगी एयरपोर्ट
|frequent_flyer=
* KrisFlyer
* PPS Club
|alliance=स्टार अलायन्स
|subsidiaries= [[Budget Aviation Holdings]]<ref>{{cite web|author=आनंद कृष्णमूर्ति आनंदकोमोरथी |url=https://www.bloomberg.com/news/articles/2016-05-18/singapore-air-forms-new-company-to-manage-two-budget-carriers |title=सिंगापुर एयर फॉर्म्स नई कंपनी दो बजट वाहक प्रबंधित |publisher=ब्लूमबर्ग |date=2016-05-17 |accessdate=2016-06-26}}</ref> <br />SilkAir<br />[[सिंगापुर Airlines Cargo]]<br />SIA Engineering Company
|net_income = {{increase}} [[Singapore dollar|S$]]804.4 million <small>(FY 2015/16)</small>
|num_employees = 24,574 <small>(FY 2015/16)</small>
|website= {{URL|https://www.singaporeair.com|singaporeair.com}}
}}
'''सिंगापुर एयरलाइंस''' लिमिटेड (एसआईए) सिंगापुर क ध्वज वाहक ह। इ विश्व क एगो प्रमुख वायुयान सेवा ह। सिंगापुर क चंगी हवाई अड्डा एकर हब (महत्वपूर्ण गतिविधिन क केंद्र) ह और दक्षिणपूर्वी एशिया, पूर्वी एशिया, [[दक्षिण एशिया]] और "कंगारू रूट" बाजारन में एकर एगो सक्षम उपस्थिति बा। इ कम्पनी पार-प्रशांत (ट्रांस-पेसिफिक) उड़ान क भी संचालन करेले, एमे दुनिया क दुगो सबसे लम्बा नॉन-स्टॉप व्यावसायिक उड़ान शामिल बाड़ी स, इ उड़ान एयरबस ए340-500 सिंगापुर से न्यू यॉर्क और लोस एंजिल्स तक जाली स।<ref>{{cite web|title=Singapore Girl - You're a Great Way To Fly".|url=http://www.singaporeair.com/en_UK/in/flying-withus/our-story/singapore-girl/|publisher=singaporeair.com Retrieved 24 April 2015}}</ref>
सिंगापुर एयरलाइंस समूह में कईगो एयरलाइन-संबंधित सहायक कंपनी शामिल बाड़ी स, एसआईए इंजीनियरिंग कंपनी नौ देशन में रखरखाव, मरम्मत और ओवरहाल (एमआरओ) कारोबार क संचालन करेले, जेमें बोइंग और रोल्स-रॉयस सहित 27 संयुक्त उद्यमन क पोर्टफोलियो बा। सिंगापुर एयरलाइंस कार्गो एसआईए की मालवाहक बेड़ा क संचालन करेला और एकर यात्री विमानन में मालवाहक क्षमता क प्रबंधन करेला। एकर दुगो सहायक बान स: सिल्कएयर द्वितीयक शहरन में क्षेत्रीय उड़ानन क संचालन करेला, जबकि स्कूट कम लागत वाला वाहक की रूप में काम करेला।<ref>{{cite web|title=एसआईए इंजीनियरिंग कंपनी बोइंग के साथ संयुक्त उद्यम को शामिल करती है|url=http://www.straitstimes.com/business/companies-markets/sia-engineering-company-incorporates-joint-venture-with-boeing|publisher=straitstimes.com}}</ref><br>
[[File:SIA Boeing 747-400, 9V-SPP, SIN 6 for wiki.jpg|thumb|Boeing 747-400 at सिंगापुर चंगी एयरपोर्ट इन स्टार अलायन्स लिवेरय व्हिले स्टिल मैनटैनिंग इतस कॉर्पोरेट लोगो ऑन द टेल|248x248px]]
सिंगापुर एयरलाइंस "सुपरजम्बो" एयरबस ए380 क शुरुआत कइलस जवन विश्व क सबसे बड़ा एयरक्राफ्ट रहे। राजस्व प्रति यात्री किलोमीटर की मामला में इ दुनिया भर की चोटी की 15 वाहकन में से एगो ह, और अंतर्राष्ट्रीय यात्री वहन की दृष्टि से दुनिया में दसवां स्थान पर बा। सिंगापुर एयरलाइंस उ एयरलाइन ह जेकर दुनिया में सबसे ज्यादा प्रशंसा कइल जाला और फोर्च्यून वर्ल्ड की 2010 की सबसे प्रशंसित कम्पनियन की रैंकिंग में एकर 27 वां स्थान बा।<ref>{{cite web|url=https://www.cleartrip.com/flight-booking/singapore-airlines.html|title=सिंगापुर एयरलाइंस|publisher=क्लेअरट्रिप.कॉम }}</ref><ref>{{cite web|title=एयरलाइन स्पॉटलाइट: सिंगापुर एयरलाइनs|url=http://www.flightnetwork.com/blog/spotlight-singapore-airlines/|publisher=flightnetwork.com|access-date=2017-08-16|archive-date=2016-04-04|archive-url=https://web.archive.org/web/20160404215802/http://www.flightnetwork.com/blog/spotlight-singapore-airlines/|url-status=dead}}</ref>
एकर सशक्त ब्रांड नांव बा और इ विमानन उद्योग में, विशेष रूप से नवाचार, सुरक्षा और सेवा में उत्कृष्टता की दृष्टि से उदाहरण प्रस्तुत करेले, साथ ही एके निरंतर लाभ भी प्राप्त होता। एके असंख्य पुरस्कार मिलल बा और विमान की खरीद की उद्योग में इ प्रथम स्थान पर बा। इ उन छह एयरलाइंस में से एगो ह जेके केथे पेसिफिक (Cathay Pacific), एशियन एयरलाइंस (Asian Airlines), मलेशियन एयरलाइंस (Malaysia Airlines), क़तर एयरवेज़ (Qatar Airways) और किंग्फिशर एयरलाइंस (Kingfisher Airlines) की साथ स्काईट्रेक्स की द्वारा 5 स्टार रेटिंग से सम्मानित कइल गइल बा।
==इतिहास==
सिंगापुर एयरलाइंस क शुरुआत 12 अक्टूबर 1937 के मलायी एयरलाइंस (एमएएल) की समावेश की साथ भइल। एकर शुरुआत लिवरपूल क ओशीन स्टीमशिप कम्पनी, सिंगापुर क स्ट्रेट्स स्टीमशिप कम्पनी और इम्पीरियल एयरवेज़ की द्वारा कइल गइल। ए एयरलाइन क पहिला उड़ान एगो चार्टर्ड उड़ान रहे जवन 2 अप्रैल 1947 के सिंगापुर की ब्रिटिश स्ट्रेट्स सेटलमेंट से कुआलालम्पुर तक गइल, एमें एयरस्पीड कौंसुल दुगो इंजन वाला एयरप्लेन क उपयोग कइल गइल। एकरी तुरंत बाद 1 मई 1947 से नियमित साप्ताहिक उड़ान सिंगापुर से कुआलालम्पुर, इपोह और पेनांग जाए लगली स, एमें एही तरह की एयरक्राफ्ट क उपयोग कइल जाव।
1940 की शेष दशक में और 1950 की दशक की दौरान अन्य ब्रिटिश कॉमनवेल्थ एयरलाइंस (जैसे की बीओएसी और क्वान्टास एम्पायर एयरवेस) की तरह इ एयरलाइन निरंतर विस्तृत होत गइल, इ तकनीकी सहायता प्रदान कइलस और आईएटीए में शामिल होखले में मदद कइलस। 1955 ले, मलायी एयरवेज क बेड़ा एतना विकसित हो गइल रहन स कि एमें बड़ी संख्या में डगलस डी सी-3 (Douglas DC-3s) शामिल हो चुकल रह न स।
==संदर्भ==
[[श्रेणी:हवाई जहाज]]
[[श्रेणी:हवाई जहाज सेवा]]
o59geh03lj2749prpv1qmqbhdk68vvo
आइस क्लाइम्बिंग
0
48943
803293
762341
2026-07-29T16:06:20Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803293
wikitext
text/x-wiki
{{italic title}}
[[File:Zinal ice climbing.webm|thumb|आइस क्लाइम्बिंग]]
'''''आइस क्लाइम्बिंग''''' (बर्फ चढ़ाई) [[पहाड़]]न पर बर्फ संरचना प चढ़ाई क गतिविधि ह। आमतौर पर, बर्फ पर चढ़ाई क मतलब होला बर्फबारी, जम्मल झरना, और जम्मल बर्फ से ढँकल चट्टानन और पत्थरन की स्लैब पर सुरक्षा और रस्सी जैसन सुविधा की साथ चढ़ाई|<ref>{{cite book |last=Chouinard |first=Yvon |title=Climbing Ice |publisher=Sierra Club Books |location=San Francisco |year=1978 |isbn= 978-0871562081}}</ref> चढ़ाई की उद्देश्यन खातिर, बर्फ के मोटे तौर पर दुगो क्षेत्र, अल्पाइन बर्फ और पानी क बर्फ में विभाजित कइल जा सकेला। अल्पाइन बर्फ पहाड़ी वातावरण में मिलेला, आमतौर पर चोटी पर पहुंचे खातिर एगो दृष्टिकोण क आवश्यकता रहेला, और अक्सर पर्वत की शिखर प एक ही प्रयास में चढ़ल जाला। पानी क बर्फ आमतौर पर चट्टान पर मिलेला या पानी के प्रवाह के नीचे रहेला। अल्पाइन बर्फ तेजी से जम जाला, जबकि पानी क बर्फ जम्मल पानी क एगो तरल प्रवाह ह।<ref name = "lowe">{{cite book |last=Lowe |first=Jeff |title=Ice World: Techniques and Experiences of Modern Ice Climbing |publisher=The Mountaineers |location=Seattle |year=1996}}</ref><ref>{{cite book |edition=First |publisher=Mountaineers Books |isbn=0-89886-769-X |last=Gadd |first=Will |author2=Roger Chayer |title=Ice & Mixed Climbing: Modern Technique |date=November 2003}}</ref>
==तकनीक==
बर्फ की ढलान और बनावट की अनुसार पर्वतारोही उपकरण चुनेला लो। उदाहरण खातिर, समतल बर्फ प, लगभग कौनों अच्छा लंबा पैदल यात्रा या पर्वतारोहण वाला बूट आमतौर पर पर्याप्त होला, लेकिन गंभीर प्रवृति की बर्फीला पर्वतारोहण खातिर डबल प्लास्टिक वाला जूता या ओईसने कठोर चमड़ा क जूता आमतौर पर इस्तेमाल कइल जाला। पतला या संकरा ढलानन प,बर्फ काट के रास्ता बनावे खातिर बर्फ क कुल्हाड़ी क उपयोग कइल जा सकेला। लंबा और तेज ढलान या ग्लेशियर यात्रा की सुरक्षित चढ़ाई की खातिर क्रैम्पन अनिवार्य होला। खड़ा बर्फ चढ़ाई क्रैम्पन और बर्फ क कुल्हाड़ी की साथ कइल जाला; पर्वतारोही बर्फ में क्रैम्पन के फंसावे खातिर पैर के बर्फ में जोर से किक करे न स और फिर कुल्हाड़ी अपने सिर के ऊपर से घुमा के बर्फ में घुसावे न स। ए तकनीक के फ्रंट पोइंटिंग कहल जाला। बर्फ क ताकत अक्सर आश्चर्यजनक होला; कुल्हाड़ी खाली एको सेंटीमीटर बर्फ में घुस जा त ऊपर खींचे खातिर पर्याप्त होले। यदि पर्वतारोही कौनों अग्रणी ह, त उन्हन के अपनी सुरक्षा खातिर चढ़ाई की रास्ता में आइस स्क्रू क इस्तेमाल करे के पड़ेला।<ref name="ropesystemsonice">{{cite web | url = http://www.climbingextreme.com/2012/01/ice-climbing-ropes-and-single-vs-half/933 | title = Climbing Extreme: Ice climbing, ropes and single vs. half | author = climbingextreme.com, Juho Risku | date = January 2012 | accessdate = 2012-01-21 | archive-date = 2012-05-19 | archive-url = https://web.archive.org/web/20120519032556/http://www.climbingextreme.com/2012/01/ice-climbing-ropes-and-single-vs-half/933 | url-status = dead }}</ref>
रॉक क्लाइम्बिंग क कुछ महत्वपूर्ण तकनीक और अभ्यास आइस क्लाइम्बिंग में सामान्य तौर पर हमेशा शामिल कइल जाला जेमें शामिल बा, रोप सिस्टम, टाइंग इन (tying in), बेलायिंग (belaying), लीडिंग (leading), अब्सेइलिंग (abseiling) और लोवेरिंग (lowering)। नौसिखआ लोगन के बर्फ पर चढ़ले की कोशिश कइले से पहिले ए तकनीकन के सीखे के चाहीं।<ref name="freedom">{{Cite book|title=Mountaineering: The Freedom of the Hills|publisher=The Mountaineers Books|location=Seattle |pages= |origyear=1960 |edition=7th |isbn=978-0898868289 |editor=Steven M. Cox and Kris Fulsaas |year=2003 |quote= |ref= }}</ref>
===रोप सिस्टम===
सिंगल, डबल और ट्विन रॉप बर्फ की चढ़ाई में इस्तेमाल होखे वाली तीनगो मुख्य रस्सी सिस्टम ह। सिंगल रोप सिस्टम, जवन सीधा चढ़ाई वाला मार्गन खातिर अनुकूल बा, इ दुनिया में सबसे अधिक इस्तेमाल कइल जाए वाला रॉक क्लाइम्बिंग सिस्टम ह। चढ़ाई में भी अक्सर डबल रॉप सिस्टम क इस्तेमाल भी कइल जाला, डबल रॉप सिस्टम, सिंगल रॉप सिस्टम की तुलना में अधिक लचीला सिस्टम होला। आखिर में, ट्विन रॉप सिस्टम, जवन की सिंगल रोप सिस्टम में दुगो जुड़वां रसरी क उपयोग करेला, ए सिस्टम क उपयोग लम्बा मल्टी-पिच रूट खातिर कइल जाला।
===टाइंग इन===
''टाइंग इन'' चढ़े वाला उपकरण में रसरी फंसवले पर विशेष जोर देला। इ तकनीक चढ़ाई की नेतृत्व चाहें या बेलायिंग की दौरान बहुत आवश्यक होला।
[[File:Canadian Rockies - Oh le tabernacle.jpg|156x240px|thumbnail|जमे हुए झरने पर कनाडाई रॉकी में चढ़ना]]
===लीडिंग===
''लीडिंग'' क मतलब चढ़ाई की दौरान नेतृत्व से होला, एमें एगो लीडर और फोलोवर क जरुरत रहेला।
===अब्सेइलिंग===
एके रेप्पेल्लिंग भी कहल जाला, ए तकनीक में एगो बांधल रस्सी की सहारे नीचे उतरल जाला।
===लोवेरिंग===
इ नीचे उतरले क एगो बहुत ही सामान्य तरीका ह।
==संदर्भ==
{{reflist}}
[[श्रेणी:एडवेंचर]]
jr0whcs03vpudcvkt7344818g4rt9hb
एडवेंचर आइलैंड
0
48964
803291
768415
2026-07-29T16:06:11Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803291
wikitext
text/x-wiki
{{Notability}}
इ क्षेत्र मेट्रो वॉक – कई तरह क अनुभव देवे वाला, '''एडवेंचर आइलैंड''' - मनोरंजन पार्क और लैगून - कृत्रिम झील की तौर पर विभाजित कइल गइल बा।
==एडवेंचर आइलैंड==
एडवेंचर आइलैंड अंतरराष्ट्रीय स्तर पर प्रसिद्ध डिजाइन सलाहकार और आर्किटेक्ट फ़ॉरेक कनाडा, मैनेजमेंट सर्विसेज कैलिफ़ोर्निया, इलेक्ट्रासोनीक यूएसए, सिटी डिज़ाइन समूह अमरीका, ड्यू कॅनडा और टीयूवी जर्मनी द्वारा डिजाइन कइल गइल एगो विश्व स्तरीय मनोरंजन पार्क ह। इ 62 एकड़ में फैलल बा। इ पार्क विश्व स्तर प स्वीकार कइल गइल उच्चतम यूरोपीय सुरक्षा मानक डीआईएन-ड्यूश इंस्टिट्यूट फॉर नॉर्मंग क पालन करेला,जवन कि दुनिया भर में शीर्ष मनोरंजन पार्कन द्वारा पालन कइल जाला।
रिठाला मेट्रो स्टेशन रोहिणी की सामने स्थित, एडवेंचर आइलैंड शहर की सब भागन से मेट्रो से और बाहरी एवं अंदरूनी रिंग रोड से जुड़ल बा।<ref>{{cite web|url=https://www.cleartrip.com/local/new%20delhi/adventure-island-amusement-park-in-new%20delhi-collection-4962-1|title=एडवेंचर आइलैंड मनोरंजन पार्क|publisher=cleartrip.com|access-date=2017-08-19|archive-date=2016-07-13|archive-url=https://web.archive.org/web/20160713074441/http://www.cleartrip.com/local/New%20Delhi/adventure-island-amusement-park-in-New%20Delhi-collection-4962-1|url-status=dead}}</ref>
==मेट्रो वॉक==
मेट्रो वॉक पूरा देश में अपनी तरह क अलग और एकमात्र परियोजना ह।<ref>{{cite web|url=http://www.appughar.com/metro-walk/|title=मेट्रो वॉक|publisher=appughar.com|access-date=2017-08-19|archive-date=2019-03-05|archive-url=https://web.archive.org/web/20190305211007/http://www.appughar.com/metro-walk/|url-status=dead}}</ref> दिल्ली और एन सी आर की फैशन की प्रति रुझान रखे वाला समझदार ग्राहक लोगन खातिर इ एगो खरीदारी क जगह ह। एकरी द्वारा दीहल जाए वाला सुविधा की आधार प मेट्रो के अपनी शहर क केवल एक मात्र फैशन खातिर मशहूर होखले की जगह क गौरव प्राप्त भइल बा।<ref>{{cite web|url=http://archivefhw.financialexpress.com/20070615/market02.shtml|title=यूनिटेक, अंतर्राष्ट्रीय मनोरंजन मनोरंजन पार्क के लिए टाई|publisher=financialexpress.com}}{{Dead link|date=October 2023 |bot=InternetArchiveBot |fix-attempted=yes }}</ref> इ ओ ग्राहक लोगन के सेवा देला जे एगो आकर्षक माहौल में शानदार खरीदी का अनुभव प्राप्त कइल चाहेला। एमें एगो मनोरंजन पार्क और एगो पूरी तरह से सुसज्जित खुदरा वाणिज्यिक केंद्र भी शामिल बा जेमें देश की कईगो ब्रांड की साथ खईले पियले क जगह भी बा, ए प्रकार से इ आगंतुकन खातिर एगो पूर्ण मनोरंजन और खरीदारी क स्थल ह। ए सबकी साथ एइजा कई तरह क मनोरंजन क कार्यक्रम क आयोजन भी कइल जाला।<br>
एइजा इंटरनेट कनेक्टिविटी क भी इन्तेजाम बा। इ नजदीकी मेट्रो स्टेशन से सिर्फ 5 मिनट की पैदल दूरी पर बा और सिर्फ 10 मिनट की ड्राइव पर एइजा से राष्ट्रीय राजमार्ग बा जवन, सोनीपत, पानीपत, अंबाला, चंडीगढ़ और अन्य उत्तरी बेल्ट्स की ओर जाला।
==सन्दर्भ==
{{reflist}}
5ap3a52gmpbaz5b5b1unvzqjqix5w77
ऊटी
0
49407
803341
781268
2026-07-29T16:22:05Z
SM7Bot
7808
/* बगइचा आ पार्क */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803341
wikitext
text/x-wiki
{{Infobox settlement
| name = ऊटी
| native_name =
| native_name_lang =
| other_name = ऊटकमंड, उदगमंडलम्
| settlement_type = [[कस्बा]] आ [[हिल स्टेशन]]
| image_skyline = Emerald Lake Nilgiris.jpg
| image_alt =
| image_caption =
| nickname = हिल स्टेशन सभ के रानी
| map_alt =
| map_caption =
| pushpin_map = India Tamil Nadu
| pushpin_label_position = right
| pushpin_map_alt =
| pushpin_map_caption =
| coordinates = {{coord|11.41|N|76.70|E|display=inline,title}}
| subdivision_type = देस
| subdivision_name = भारत
| subdivision_type1 = राज्य
| subdivision_type2 = जिला
| subdivision_name1 = [[तमिल नाडु]]
| subdivision_name2 = [[नीलगिरी जिला]]
| established_title =
| established_date =
| founder =
| named_for =
| unit_pref =
| area_footnotes =
| area_total_km2 =
| area_rank =
| elevation_footnotes = <ref name="municipality">{{cite web|url=http://municipality.tn.gov.in/ooty/abt-muni.htm |title=About Municipality |access-date=15 February 2008 |publisher=municipality.tn.gov.in |url-status=dead |archiveurl=https://web.archive.org/web/20080115070843/http://municipality.tn.gov.in/ooty/abt-muni.htm |archivedate=15 January 2008 |df= }}</ref>
| elevation_m = 2240
| population_total = 88,430
| population_as_of = 2011
| population_footnotes = <ref>{{Cite web|url=http://www.censusindia.gov.in/pca/SearchDetails.aspx?Id=688772|title=Census 2011|publisher=GOI}}</ref>
| population_density_km2 = auto
| population_rank =
| population_demonym =
| demographics_type1 = भाषा
| demographics1_title1 = ऑफिशियल
| timezone1 = [[भारतीय मानक समय|IST]]
| utc_offset1 = +5:30
| postal_code_type = [[पिनकोड]]
| postal_code = 643 001
| area_code = 91423
| area_code_type = टेलीफोन
| registration_plate = TN 43
| website =
}}
'''उदगमंडलम्''' चाहे '''ऊटी''' ({{Lang|en|Ooty}}) [[तमिलनाडु]] में एगो [[कस्बा]] आ म्युनिस्पैलिटी बा आ ई एगो परसिद्ध [[हिल स्टेशन|हिल इस्टेशन]] ह। ई [[नीलगिरी जिला|नीलगिरि जिला]] के मुख्यालय भी बा। [[नीलगिरि|नीलगिरी]] के पहाड़ी पर {{convert|2240|m}} के ऊँचाई पर मौजूद ई जगह एगो परसिद्ध पहाड़ी पर्यटन स्थल बा। कोयंबटूर से 86 किमी उत्तर आ मैसूर से 158 किमी दक्खिन में मौजूद ई कस्बा नीलगिरि घाट सड़क आ नीलगिरि माउंटेन रेलवे से जुड़ल बाटे आ गर्मी के सीजन खातिर एगो पापुलर पर्यटन केंद्र हवे। 2011 में एह कस्बा के कुल आबादी 88,430 रहल।
मूल रूप से ई एरिया टोडा लोग के रहल जे अठारहवीं सदी के अंत के समय में ईस्ट इंडिया कंपनी के कब्जा में आइल। एहिजे के अर्थब्यवस्था पर्यटन आ खेती-किसानी पर टिकल बाटे, एकरे साथे-साथ कुछ किसिम के दवाई आ फोटोग्राफिक फिल्म (रील) के मैनुफैक्चरिंग एहिजे होखे ला; कई ठो फिलिम सभ के शूटिंग के लोकेशन के रूप में भी एह जगह के बीछल जा चुकल बा। एकरा के पॉपुलर नाँव "क्वीन ऑफ़ हिल स्टेशंस" (हिल स्टेशन सभ के रानी) आ "क्वीन ऑफ़ वेस्टर्न घाट्स" (पच्छिमी घाट के रानी) से बोलावल जाला। ई मद्रास प्रेसिडेंसी के गर्मी के सीजन के राजधानियो रहल।
== नाँव ==
एकरे नाँव के बारे में सटीक जानकारी नइखे। नाँव के पहिला लिखित परमान मार्च 1821 में ''मद्रास गैजेट'' नाँव के अखबार के लिखल एगो चिट्ठी में ''Wotokymund'' हिज्जे के साथे मिले ला। मानल जाला कि पुराना समय में ई ''ओट्टकल मंडू'' कहल जाय। इहो अनुमान लगावल जाला कि नाँव उदगमंडलम रहे जे बाद में, ब्रिटिश काल में, बदल के ऊटकमंड भ गइल, जे अउरियो बाद में छोट हो के ऊटी भइल।
नाँव के पहिला हिस्सा संभव बा कि नीलगिरी परबत के बिचला हिस्सा के लोकल नाँव के बिगड़े से बनल हवे। ऊट्टा-कल के शाब्दिक अरथ "एक ठो पाथर" होखे ला। हो सके ला कि ई कौनों अइसन पबित्र पाथर रहल होखे जे लोकल टोडा लोग द्वारा मानल जात होखे। दुसरा हिस्सा मंड टोडा भाषा के मांडू शब्द के बिगड़ल रूप हवे जेकर अरथ गाँव होला।
ऊटी नीलगिरि के परबत में बा; खुद नीलगिरि (नीला परबत) कुरुंजी नाँव के एगो फूल से आवे ला जे एह परबत पर लमहर समय (बारह बरिस) के बाद फुलाला आ पूरा ढाल सभ के नीलछाहूँ क देला।
== इतिहास ==
उदगमंडलम् मूल रूप से आदिवासी लोगन के इलाका रहल आ एहिजे तमिल, टोडा, कोटा, ईरुला, आ कुरुंबा जनजाति के लोग रहे।
1818 में असिस्टेंट जे. सी. विश आ एन. डब्लू. किंडर्सले लोग जे जॉन सुलिवान, ओह समय के कोयंबटूर के जिला कलेक्टर, के सहजोगी रहल, ऊटी के जात्रा कइलें लोग आ रिपोट जमा कइल लोग। सुलिवान कोटागिरी के उत्तर में दिम्बाती में जनवरी 1819 में कैंप कइलें आ एकरे सुन्दरता के चकित रहि गइलें। मई 1819 में टोडा लोग एह क़स्बा के कुछ हिस्सा सुलिवान के दे दिहल आ ऊ दिम्भाती में आपन बंगला बनवावे शुरू कइलें। एही साल ऊ सिरुमुगाई से दिम्भाती खातिर रोड के निर्माण सुरू करववलें रो मई 1823 में बन के तइयार भइल आ बाद में 1830-32 में कुन्नूर ले बढावल गइल।
अरानमोर महल जे ऊटी में बा, मद्रास प्रेसिडेंसी के राजधानी के रूप में काम करे गर्मी के सीजन खाती।ई ब्रिटिश अधिकारी लोग एहिजे आवे जब गर्मी के सीजन होखे आ ई पापुलर रिजार्ट बन गइल। सिपाही लोगन के तरोताजा होखे खातिर वेलिंगटन भेजल जाय। अब्बो वेलिंगटन भारतीय सेना के मदरास रेजिमेंट के घरेलू अस्थान हवे। भारत के आजादी के बाद ऊटी के हिल स्टेशन के रूप में बिकसित कइल गइल।
== भूगोल ==
=== लोकेशन ===
ऊटी [[नीलगिरि बायोस्फीयर रिजर्व]] में पड़े ला। कई सारा जंगली एरिया आ जलभंडार सभ तक आम पहुँच ना बाटे जे एह इलाका के नाजुक इकोसिस्टम के संरक्षित रखे खातिर सुरक्षित कइल गइल बाड़ें। बायोस्फीयर रिजर्व के कुल इलाका के कुछ हिस्सा चिन्हित कइल गइल बाड़ें जहाँ पर्यटन खातिर आवाजाही हो सके ला। साथे साथ अइसनो उपाय कइल गइल बाड़ें कि पर्यटक लोगन खातिर खुला रखे के संघे एह इलाका के सुरक्षो कइल जा सके।<ref>{{cite news|title= Ban on carry bags|url = http://www.hindu.com/2005/02/04/stories/2005020401370300.htm|archive-url = https://web.archive.org/web/20050220103729/http://www.hindu.com/2005/02/04/stories/2005020401370300.htm|url-status = dead|archive-date = 20 February 2005| location=Chennai, India|work= [[The Hindu]]|date=4 February 2005}}</ref>
ऊटी [[नीलगिरि]] के पहाड़ी पर [[समुंद्र तल]] से {{convert|2240|m|ft|abbr=off}} के ऊँचाई पर बसल कस्बा हवे।<ref name="municipality"/>
=== जलवायु ===
[[File:Boat House in Ooty Lake, Ooty - Tamil Nadu.JPG|thumb|upright=1.2|ऊटी झील में एगो बोटहाउस]]
ऊटी के जलवायु [[उपोष्णकटिबंधी हाइलैंड जलवायु]] वाला इलाका हवे जे [[कोपेन के जलवायु वर्गीकरण|कोपेन के जलवायु बर्गीकरण]] में Cwb प्रकार में आवे ला।<ref name="Climate-Data.org">{{cite web|title=Climate: Ooty – Climate graph, Temperature graph, Climate table|url=http://en.climate-data.org/location/24046/|publisher=Climate-Data.org|access-date=30 September 2015 | archive-url=https://web.archive.org/web/20140227100008/http://en.climate-data.org/location/24046/ |archive-date=27 February 2014}}</ref> भले ई [[उष्णकटिबंध]] के क्षेत्र में होखे, एकरे बावजूद इहाँ के मौसम साल भर सुहावन आ [[बसंत]] नियर रहे ला। हालाँकि, जनवरी-फरवरी में रात के तापमान ठंढा होखे ला। सालों भर औसत तापमान लगभग बरोबर रहे ला; सालाना औसत हाई टेम्परेचर {{convert|17|-|20|C|F|0}} आ औसत लो टेम्परेचर लगभग {{convert|5|-|12|C|F|0}} के बीचा में रहे ला।
अबतक ले के सभसे बेसी रिकार्ड तापमान ऊटी में {{convert|28.5|C|F}} रहल बा जे एह उष्णकटिबंधी क्षेत्र में पड़े वाला जगह खातिर कम्मे बाटे आ [[दक्खिन एशिया|दक्खिनी एशिया]] के हिसाब से एकरा के ठंढा मानल जा सके ला। [[बरसात]] के सीजन एहिजा ठंढा आ तेज बयार भरल रहे ला। हवा के चलते ठंढा (विंड चिल प्रभाव) दिन में {{convert|5|C|F}} के एहसास करावे वाला हो सके ला। हवा एहिजा सालों भर तेज बहे ले। इहाँ रिकार्ड कइल गइल सभसे कम तापमान {{convert|-2|C|F}} रहल बा।<ref>{{cite web |url=http://citypatriots.com/asia/india/tamil-nadu/ooty |title=Ooty in Tamil Nadu |url-status=dead |archive-url=https://web.archive.org/web/20160305184727/http://citypatriots.com/asia/india/tamil-nadu/ooty |archive-date=5 March 2016}}</ref> एहिजे सालाना औसत बरखा {{convert|1050|mm|in}} होखे ला आ दिसंबर से मार्च के मौसम सूखल रहे ला।
{{Weather box
| location = Ooty (Udhagamandalam) 1981–2010, extremes 1901–2012
| width = auto
| metric first = Yes
| single line = Yes
| Jan record high C = 26.8
| Feb record high C = 27.3
| Mar record high C = 27.5
| Apr record high C = 28.5
| May record high C = 28.0
| Jun record high C = 26.1
| Jul record high C = 22.8
| Aug record high C = 23.0
| Sep record high C = 23.3
| Oct record high C = 25.5
| Nov record high C = 25.4
| Dec record high C = 27.4
| year record high C = 28.5
| Jan high C = 21.4
| Feb high C = 22.1
| Mar high C = 23.2
| Apr high C = 23.3
| May high C = 22.9
| Jun high C = 18.7
| Jul high C = 17.7
| Aug high C = 17.7
| Sep high C = 19.2
| Oct high C = 19.3
| Nov high C = 19.5
| Dec high C = 20.9
| year high C = 20.5
| Jan low C = 6.2
| Feb low C = 7.1
| Mar low C = 9.3
| Apr low C = 11.2
| May low C = 11.8
| Jun low C = 11.4
| Jul low C = 11.1
| Aug low C = 11.2
| Sep low C = 10.7
| Oct low C = 10.4
| Nov low C = 9.0
| Dec low C = 7.1
| year low C = 9.7
| Jan record low C = -2.1
| Feb record low C = 0.0
| Mar record low C = 1.1
| Apr record low C = 5.0
| May record low C = 4.4
| Jun record low C = 2.2
| Jul record low C = 2.5
| Aug record low C = 4.6
| Sep record low C = 4.4
| Oct record low C = 0.0
| Nov record low C = -1.1
| Dec record low C = -1.1
| year record low C = -2.1
| rain colour = green
| Jan rain mm = 9.7
| Feb rain mm = 7.4
| Mar rain mm = 20.6
| Apr rain mm = 73.1
| May rain mm = 98.7
| Jun rain mm = 124.1
| Jul rain mm = 149.4
| Aug rain mm = 97.9
| Sep rain mm = 132.1
| Oct rain mm = 169.6
| Nov rain mm = 110.4
| Dec rain mm = 47.7
| year rain mm = 1040.9
| Jan rain days = 1.1
| Feb rain days = 0.6
| Mar rain days = 1.9
| Apr rain days = 5.6
| May rain days = 8.7
| Jun rain days = 10.7
| Jul rain days = 11.2
| Aug rain days = 10.0
| Sep rain days = 10.1
| Oct rain days = 11.7
| Nov rain days = 7.6
| Dec rain days = 3.3
| year rain days = 82.4
|time day = 17:30 [[भारतीय मानक समय|आइएसटी]]
| Jan humidity = 65
| Feb humidity = 62
| Mar humidity = 59
| Apr humidity = 66
| May humidity = 73
| Jun humidity = 84
| Jul humidity = 86
| Aug humidity = 87
| Sep humidity = 84
| Oct humidity = 84
| Nov humidity = 82
| Dec humidity = 72
|year humidity = 75
|source 1 = [[India Meteorological Department]]<ref name=IMDnormals>
{{cite web
| archive-url = https://web.archive.org/web/20200205040301/http://imdpune.gov.in/library/public/1981-2010%20CLIM%20NORMALS%20%28STATWISE%29.pdf
| archive-date = 5 February 2020
| url = https://imdpune.gov.in/library/public/1981-2010%20CLIM%20NORMALS%20%28STATWISE%29.pdf
| title = Station: Octacamund Climatological Table 1981–2010
| work = Climatological Normals 1981–2010
| publisher = India Meteorological Department
| date = January 2015
| pages = 573–574
| access-date = 1 March 2020}}</ref><ref name=IMDextremes>
{{cite web
| archive-url = https://web.archive.org/web/20200205042509/http://imdpune.gov.in/library/public/EXTREMES%20OF%20TEMPERATURE%20and%20RAINFALL%20upto%202012.pdf
| archive-date = 5 February 2020
| url = https://imdpune.gov.in/library/public/EXTREMES%20OF%20TEMPERATURE%20and%20RAINFALL%20upto%202012.pdf
| title = Extremes of Temperature & Rainfall for Indian Stations (Up to 2012)
| publisher = India Meteorological Department
| date = December 2016
| page = M207
| access-date = 1 March 2020}}</ref>
}}
{{clear}}
==देखे-घूमे लायक जगह==
ऊटी एगो लोकप्रिय पर्यटन स्थल हवे जहाँ शहर आ आसपास के कई जगह बाड़ी सऽ।
===बगइचा आ पार्क===
सरकारी गुलाब के बगीचा (पहिले सेंटेनरी गुलाब पार्क) भारत के सभसे बड़ गुलाब के बगीचा हवे। ई ऊटी शहर के विजयनगरम में एल्क हिल के ढलान पर स्थित बा। 2,200 मीटर (7,200 फीट) के ऊंचाई पर बा। आज ई बगीचा देश के सभसे बड़ गुलाब के संग्रह में से एक बाटे जहाँ 2,800 कल्टीवेर सभ के 20,000 से ढेर किसिम के गुलाब के संग्रह बाटे। एह संग्रह में हाइब्रिड टी गुलाब, मिनीएचर रोज़, पॉलियान्थास, पपाजेना, फ्लोरिबुंडा, रैम्बलर्स, याकिमोर आ करिया आ हरियर जइसन असामान्य रंग के गुलाब शामिल बा.
8.9 हेक्टेयर (22 एकड़) के ऊटी वनस्पति उद्यान के बिस्तार 1847 में भइल आ एकर रखरखाव तमिलनाडु सरकार द्वारा कइल जाला। वनस्पति उद्यान रसीला, हरियर, आ बढ़िया से रखरखाव कइल गइल बा। हर मई में दुर्लभ पौधा प्रजाति के प्रदर्शनी के संगे फूल शो के आयोजन कइल जाला। एह बगीचा सभ में लगभग एक हजार प्रजाति के पौधा, झाड़ी, फर्न, पेड़, हर्बल आ बोनसाई पौधा सभ के बिदेसी आ देशी दुनों प्रजाति पावल जालीं। एह बगीचा में 20 मिलियन साल पुरान जीवाश्म वाला पेड़ बा।
हिरण पार्क ऊटी झील के किनारे स्थित बा। उत्तराखंड के नैनीताल के चिड़ियाघर के छोड़ के ई भारत के सभसे ऊँच ऊँचाई वाला चिड़ियाघर हवे। ई पार्क कई प्रजाति सभ के हिरण आ अउरी जानवर सभ के रखे खातिर बनावल गइल
== संदर्भ ==
{{Reflist|35em}}
==अउरी पढ़े खातिर==
* {{cite book |title=Decaying splendours: two palaces: reflections in an Indian mirror |last=Weeks |first=Stephen |year=1979 |publisher=British Broadcasting Corporation |location=University of California |isbn=978-0-563-17516-2 |url=https://books.google.com/books?id=gTo2AAAAIAAJ&q=Decaying+Splendours;+two+palaces:+reflections+in+an+Indian+mirror|access-date=19 August 2011}}
== बाहरी कड़ी ==
* [http://www.nilgiris.tn.gov.in/tour_ooty.html ऑफिशियल इतिहास आ पर्यटन वेबसाइट]{{Dead link|date=September 2023 |bot=InternetArchiveBot |fix-attempted=yes }} {{In lang|en}}
{{Authority control}}
[[श्रेणी:तमिल नाडु के शहर आ कस्बा]]
[[श्रेणी:भारत के हिल स्टेशन]]
90j93a568ktg3w9osesu63peulgi9g8
दक्षिण अफ़्रीका एयरवेज
0
49440
803344
762661
2026-07-29T16:22:17Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803344
wikitext
text/x-wiki
{{Infobox airline
| airline = South African Airways
| logo =
| IATA = SA
| ICAO = SAA
| callsign = SPRINGBOK
| logo_size = 200
| founded = {{Start date and age|1934|02|01|df=y}}
| focus_cities = {{nowrap|Cape Town International Airport}}
| hubs = {{nowrap|O.R. Tambo International Airport}}
| frequent_flyer = Voyager
| alliance = Star Alliance
| fleet_size = 53
| destinations = 42
| parent = Government of South Africa
| company_slogan = ''Bringing the world to Africa and taking Africa to the world''
| headquarters = OR Tambo International Airport<br>Kempton Park, Gauteng, Ekurhuleni Metropolitan Municipality, Gauteng, South Africa
| key_people = Musa Zwane (Acting CEO)
| revenue = {{increase}} [[South African rand|R]]27,1 billion (2012/13 <abbr title="Fiscal year">FY</abbr>)<ref name="defenceweb.co.za">{{cite web|url=http://www.defenceweb.co.za/index.php?option=com_content&view=article&id=33511:saa-revenue-up-but-losses-continue-for-2013&catid=114:civil-aviation&Itemid=247|title=SAA revenue up but losses continue for 2013}}</ref>
| operating_income = {{decrease}}South African rand 60 million (2012/13 <abbr title="Fiscal year">FY</abbr>)
<ref>{{cite web|url=http://www.flysaa.com/bi/ja/Documents/Financials/SAA_AR_2012_web.pdf|title=Fiscal|publisher=flysaa.com}}</ref>
| profit = {{decrease}} South African rand-991 million Loss (2012/13 <abbr title="Fiscal year">FY</abbr>)<ref name="defenceweb.co.za"/>
| assets ={{decrease}} [[South African rand|R]]14,044 million (2009/10 <abbr title="Fiscal year">FY</abbr>)<ref name=SAA_2010_Annual_Report>{{cite web|title=Sustain Profitability: Annual Report 2010|publisher=South African Airways|url=https://www.flysaa.com/pv_obj_cache/pv_obj_id_5D76D3D217737135BBB491607A891BB54A324000/filename/SAAAnnualReport2010.pdf|accessdate=31 December 2010}}</ref>{{rp|37}}
| subsidiaries =Mango
|num_employees = 8,525 (2016)<ref>{{cite web|url=http://www.staralliance.com/en/member-airline-details?airlineCode=SA|accessdate=29 March 2017|title=South African Airways|date=नवंबर 2016}}</ref>
| website ={{URL|https://www.flysaa.com}}
}}
दक्षिण अफ़्रीकी एयरवेज (एसएए) दक्षिण अफ्रीका का ध्वजवाहक ह। एकर मुख्यालय ऑर तामो अंतरराष्ट्रीय हवाई अड्डा की एयरवेज पार्क प बा, जवन की केम्पटन पार्क, एकूरहुलेनी, गौतेंग में बा। इ एयरलाइन, एसए एक्सप्रेस, एसए एयरलाइन और एकर कम लागत वाली वाहक, मैंगो की साथ साझेदारी में, दक्षिण अफ्रीका की अन्दर और पूरा महाद्वीप में, और एकरी जोहान्सबर्ग हब ऑर तामो अंतरराष्ट्रीय हवाई अड्डा से आठ अंतरमहाद्वीपीय मार्गन प एकरी 54 एयरक्राफ्ट की बेड़ा की साथ उड़ान भरेले। मूसा झवेन नवंबर 2015 से एकर कार्यकारी सीईओ हंवं।<ref>{{cite web| url=https://www.flysaa.com/about-us/leading-carrier/about-saa/brief-history| title= दक्षिण अफ़्रीकी एयरवेज के बारे में|publisher=flysaa.com}}</ref>
दक्षिण अफ्रीकी सरकार द्वारा यूनियन एयरवेज क अधिग्रहण की बाद 1934 में दक्षिण अफ्रीकी एयरवेज क स्थापना भइल रहे। ए एयरलाइन क शुरूआत में दक्षिण अफ्रीकी रेलवे और हार्बर्स प्रशासन द्वारा देखरेख और नियंत्रण कइल गइल। अफ्रीकी देशन द्वारा रंगभेद विरोधी प्रतिबंधन से रंगभेद की दौरान ठहराव वाला हवाईअड्डन से एयरलाइन वंचित हो गइली स, जेसे उन्हन के ए महाद्वीप के लंबा दूरी की विमानन की साथ बाईपास करे के मजबूर होखे के पड़ल। ए समय की दौरान, एके एकरी अफ्रीकी नांव, सुद-अफ्रिकाएनस ल्यूगेंस (एसएएल) द्वारा भी जानल जाव, जेके बाद में हटा दिहल गइल। 1997 में एसएए आपन नांव, छवि और विमान क वेशभूषा बदल दिहलस और ऑनलाइन टिकट सेवा शुरू कइलस। 2006 में, एसएए एगो स्वतंत्र एयरलाइन की रूप में संचालित करे खातिर ट्रांसनेट, जवन की ओकर मूल कंपनी रहे ओसे अलग हो गइल। एसएए टेनिस पेशेवरन की एसोसिएशन क आधिकारिक एयरलाइन ह। SAA, एगो कम लागत वाली घरेलू एयरलाइन मैंगो क मालिक ह, और ऐयरलिंक और दक्षिण अफ्रीकी एक्सप्रेस की साथ संबंध स्थापित कइले बा।इ स्टार अलायंस क सदस्य ह।
==इतिहास==
[[File:Junkers-f13.jpg|thumb|एसएए ने कई संयुग्मित यूनियन एयरवेज के विमानों के साथ आपरेशन की शुरुआत की, जिसमें चित्रित चित्र जैसा जंकर्स एफ.13 भी शामिल है]]
दक्षिण अफ्रीकी सरकार द्वारा यूनियन एयरवेज की अधिग्रहण की बाद 1 फरवरी 1934 के दक्षिण अफ्रीकी एयरवेज क गठन भइल। चालीस कर्मचारी सदस्य, एगो डे हवललैंड DH.60 जिप्सी मॉथ, एगो डे हैविलैंड 80ए पुस मॉथ, तीनगो जंकर एफ-13एस और एगो पट्टा पर लिहल जंकरर्स एफ 13 और जंकर्स ए50 अधिग्रहण में शामिल रह न स। अधिग्रहण की बाद, सरकार एयरलाइन क नांव बदल के दक्षिण अफ्रीकी एयरवेज क दिहलस और एकर नियंत्रण दक्षिण अफ्रीकी रेलवे और हार्बर प्रशासन (अब ट्रांसनेट) के दे दिहलस। चार्टर परिचालन क ओ ही वर्ष में शुरुआत हो गइल रहे। अगिला वर्ष में 1 फरवरी के, इ विमान कम्पनी स्वीड्स ल्यूगेंस / दक्षिण पश्चिम एयरवेज (अब एयर नामीबिया) के अधिग्रहण कइलस, जवन एकरी पहिले 1932 से विंडहोक और किमबर्ली की बीच साप्ताहिक एयर-मेल क सेवा प्रदान करे।<ref>{{cite web|url=https://www.flightglobal.com/pdfarchive/view/1966/1966%20-%201219.html|title= SOUTH AFRICAN AIRWAYS|publisher=Flight International}}</ref><ref>{{cite web| url=https://www.cleartrip.com/flight-booking/south-african-airlines.html| title=दक्षिण अफ़्रीकी एयरवेज| publisher=क्लेअरट्रिप. कॉम}}</ref>
ए समय की दौरान, दक्षिण अफ्रीकी एयरलाइन तीनगो जंकर्स जू 52/3 एम विमान क ऑर्डर दिहलस, जवन की अक्टूबर 1934 में मिलल और 10 दिन बाद सेवा में उतारल गइल। ए विमानन के चार चालक दल की साथ 14 यात्री ले जाए खातिर कॉन्फ़िगर कइल गइल रहे। इन्हन से डरबन-ईस्ट लंदन-पोर्ट एलिजाबेथ-जॉर्ज / मोसेल बे-केप टाउन मार्ग पर साप्ताहिक सेवा की साथ तीन बेर साप्ताहिक डरबन-जोहान्सबर्ग सेवा सक्षम भइल।<ref>{{cite web|url=http://active.boeing.com/commercial/orders/displaystandardreport.cfm?cboCurrentModel=707&optReportType=AllModels&cboAllModel=707&ViewReportF=View+Report|title=707 Model Summary|publisher=Boeing Commercial Airplanes|access-date=2017-09-06|archive-date=2015-09-04|archive-url=https://web.archive.org/web/20150904102020/http://active.boeing.com/commercial/orders/displaystandardreport.cfm?cboCurrentModel=707&optReportType=AllModels&cboAllModel=707&ViewReportF=View+Report|url-status=dead}}</ref>
1 जुलाई 1935 के, SAA आपन संचालन रैंड हवाई अड्डा प स्थानांतरित क दिहलस काहेंकी इ तेजी से स्पष्ट हो गइल रहे की जोहान्सबर्ग देश का विमानन केन्द्र बन जाई, जवन की रैंड-डरबन-ईस्ट लंदन-पोर्ट एलिजाबेथ-केप टाउन सेवा की शुभारंभ की साथ भइल।
==सन्दर्भ==
{{reflist}}
[[श्रेणी:हवाई जहाज]]
[[श्रेणी:हवाई जहाज सेवा]]
j5w52xo4tla42fsqrfwog1euyotyicy
पश्चिम एक्सप्रेस
0
49514
803330
762750
2026-07-29T16:21:22Z
SM7Bot
7808
/* रेल इंजन */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803330
wikitext
text/x-wiki
{{Infobox rail service
| name = पश्चिम एक्सप्रेस
| image= 12925 Paschim Express.jpg
| caption =
| type = सुपरफास्ट एक्सप्रेस
| states = महाराष्ट्र, गुजरात, मध्य प्रदेश, राजस्थान, उत्तर प्रदेश, हरियाणा, दिल्ली, पंजाब
| first = 24 December 1956
| last =
| operator = पच्छिमी रेलवे जोन
| ridership =
| start = बांद्र टर्मिनस
| stops = 42
| end =अमृतसर जंक्शन
| distance = {{convert|1821|km|0|abbr=on}}
| journeytime = 31 घंटा 10 मिनट
| frequency = रोज
| class = एसी 1ली श्रेणी, एसी 2 टायर, एसी 3 टायर, स्लीपर दर्जा, जनरल दर्जा
| seating = Yes
| sleeping = Yes
| catering = मौजूद, पैंट्री कार से
| observation =
| entertainment =
| baggage =
| other facilities=
| stock = Standard Indian Railway coaches
| gauge = {{RailGauge|1676mm|allk=on}}
| el =
| trainnumber = 12925 / 12926
| speed = {{convert|110|km/h|0|abbr=on}} अधिकतम <br/>{{convert|58.43|km/h|0|abbr=on}}, हाल्ट जोड़ के
| map = [[File:Paschim Express (Amritsar - Mumbai) Route map.jpg|250px]]
| map_state =
}}
'''12925 / 12926 पश्चिम एक्सप्रेस''' भारतीय रेलवे क एगो सुपरफास्ट एक्सप्रेस ट्रेन ह और इ पंजाब में बांद्रा टर्मिनस और अमृतसर की बीच में चलेले। एमें 22925/26 क स्लीप कोच होन स जवन कि कालका की ओर जान स। इ ट्रेन दैनिक सेवा देले, इ बांद्रा टर्मिनस से अमृतसर ले रेल संख्या 12925 और वापसी में ट्रेन नंबर 12926 की रूप में संचालित होले।
==कोच==
12925/26 पश्चिम एक्सप्रेस में 1गो एसी प्रथम श्रेणी, 3गो एसी 2 टीयर, 5गो एसी 3 टीयर, 8गो स्लीपर क्लास, 3गो जनरल अनारक्षित कोच और 4 जनरल तथा समान कोच होंन स।
भारत की अधिकतर ट्रेन सेवा की तरह, मांग की अनुसार भारतीय रेलवे अपनी विवेक की अनुसार एकरी कोच संरचना में बदलाव क सकेला।
==सेवा==
पश्चिम एक्सप्रेस मुंबई और अमृतसर, कालका की बीच चलेले। अम्बाला कैंट जंक्शन से ए ट्रेन क दुगो भाग हो जाला। इ ट्रेन दैनिक सेवा देले जवने की तहत 31 घंटा 45 मिनट में 1821 किलोमीटर क दूरी 12925 पश्चिम एक्सप्रेस की रूप में 57.35 किमी/घंटा की औसत रफ्तार से और 30 घंटा 35 मिनट में 12926 पश्चिम एक्सप्रेस की रूप में तय करेले और ए दौरान एकर औसत रफ़्तार 59.54 किमी/घंटा होला। कुल दूरी तय कइले क औसत गति (बांद्रा टर्मिनस - अमृतसर और वापसी क) 58.43 किमी/घंटा बा।
==रेल इंजन==
पहिले WCAM 1 या WCAM 2/2P जैसन दुगो इंजन बांद्रा टर्मिनस और वडोदरा की बीच ए ट्रेन के चलावत रहन हं स और ओकरी बाद गाजियाबाद स्थित WAP 1 इंजन द्वारा शेष यात्रा पूरा कइल जात रहल ह।
जब पश्चिम रेलवे 5 फरवरी 2012 के डीसी इलेक्ट्रिक कनवर्ज़न के एसी में बदलले क काम पूरा कइलस, तबसे गाजियाबाद की WAP 7 चाहें WAP 5 इंजन द्वारा सम्पूर्ण यात्रा पूरा कइल जाला।
==ट्रेन क इतिहास==
* पश्चिम एक्सप्रेस के अनौपचारिक रूप से पश्चिम सुपर डीलक्स एक्सप्रेस की रूप में जानल जाला।
* इ मुंबई सेंट्रल से बांद्रा टर्मिनस स्थानांतरित होखे वाली पहिला कुछ गाड़ी में से एगो रहे।
* जब इ 12926 पश्चिम एक्सप्रेस की रूप में संचालित होले तब इ हज़रत निजामुद्दीन स्टेशन प नाहीं रुकेले।
{{clear}}
==गैलरी==
<gallery>
File:WAP 7 engine of the 12926 Paschim Express at Bandra Terminus.jpg|बांद्रा टर्मिनस में 12 9 26 पश्चिम एक्सप्रेस के वैप 7 इंजन
File:AC 2 tier - 2925 Paschim Express.jpg|एसी 2 स्तरीय - 2 9 25 पश्चिम एक्स्प्रेस
File:AC 1st Class - 2925 Paschim Express.jpg|एसी प्रथम श्रेणी - 2 9 25 पश्चिम एक्स्प्रेस
File:12925_Paschim_Express_at_Borivali_railway_station.jpg|12 9 5 पश्चिम बोरिवली रेलवे स्टेशन पर पश्चिम एक्स्प्रेस
File:2925 Paschim Express with a WAP 1 engine.JPG|एक डब्लूएपी 1 इंजन के साथ 2925 पश्चिम एक्सप्रेस
File:22925 Paschim Express.jpg|22925 पश्चिम एक्सप्रेस
File:22925 Paschim Express - AC 3 tier.jpg|22925 पश्चिम एक्सप्रेस - AC 3 टियर
File:22925 Paschim Express at Borivali.jpg|22925 पश्चिम में एक्सप्रेस बोरिवली रेलवे स्टेशन
File:12926 Paschim Express at Bandra Terminus.jpg|22926 पश्चिम में एक्सप्रेस बांद्रा टर्मिनस
</gallery>
==टाइम टेबल==
12925 पश्चिम एक्सप्रेस बांद्रा टर्मिनस से हर दिन 12.00 बजे प्रस्थान करेले और अगिला दिन 19.20 बजे अमृतसर पहुंचेले। वापसी में, 12926 पश्चिम एक्सप्रेस अमृतसर से हर दिन 08:10 बजे प्रस्थान करेले और अगिला दिन 14.45 बजे बांद्रा टर्मिनस पहुंचेले।<ref>{{cite web| url=http://www.irfca.org/faq/faq-seltrain.html#other| title=प्रसिद्ध गाड़ियों| publisher=irfca.org}}</ref><ref>{{cite web| url=https://www.cleartrip.com/trains/12926| title=पश्चिम एक्सप्रेस| publisher=क्लेअरट्रिप. कॉम| access-date=2017-09-13| archive-date=2017-03-27| archive-url=https://web.archive.org/web/20170327171816/https://www.cleartrip.com/trains/12926| url-status=dead}}</ref>
{| class="wikitable plainrowheaders unsortable" style="text-align:center"
|-
!scope="col" rowspan="2" style="background:gold;"|स्टेशन कोड
!scope="col" rowspan="2" style="background:gold;"|स्टेशन का नाम
!scope="col" colspan="2" style="background:gold;"|12925 - बांद्रा टर्मिनस से अमृतसर
!scope="col" rowspan="2" style="background:gold;"|दूरी स्रोत से किमी में
!scope="col" rowspan="2" style="background:gold;"|दिन
!scope="col" colspan="2" style="background:gold;"|12926 - अमृतसर से बांद्रा टर्मिनस तक
!scope="col" rowspan="2" style="background:gold;"|दूरी स्रोत से किमी में
!scope="col" rowspan="2" style="background:gold;"|दिन
|-
!scope="col" colspan="1" style="background:gold;"|आगमन
!scope="col" colspan="1" style="background:gold;"|प्रस्थान
!scope="col" colspan="1" style="background:gold;"|आगमन
!scope="col" colspan="1" style="background:gold;"|प्रस्थान
|-
| valign="center" |BDTS
| valign="center" |बांद्रा टर्मिनस
| valign="center" |स्रोत
| valign="center" |12:00
| valign="center" |0
| valign="center" |1
| valign="center" |14:45
| valign="center" |गंतव्य
| valign="center" |1821
| valign="center" |2
|-
| valign="center" |BRC
| valign="center" |वडोदरा जंक्शन
| valign="center" |18:03
| valign="center" |18:13
| valign="center" |381
| valign="center" |1
| valign="center" |08:16
| valign="center" |08:26
| valign="center" |1440
| valign="center" |2
|-
| valign="center" |RTM
| valign="center" |रतलाम जंक्शन
| valign="center" |22:20
| valign="center" |22:30
| valign="center" |642
| valign="center" |1
| valign="center" |04:00
| valign="center" |04:10
| valign="center" |1179
| valign="center" |2
|-
| valign="center" |KOTA
| valign="center" |कोटा जंक्शन
| valign="center" |02:05
| valign="center" |02:15
| valign="center" |909
| valign="center" |2
| valign="center" |23:30
| valign="center" |23:40
| valign="center" |913
| valign="center" |1
|-
| valign="center" |GGC
| valign="center" |गंगापुर सिटी
| valign="center" |04:40
| valign="center" |04:42
| valign="center" |1081
| valign="center" |2
| valign="center" |21:13
| valign="center" |21:15
| valign="center" |741
| valign="center" |1
|-
| valign="center" |NDLS
| valign="center" |नई दिल्ली
| valign="center" |10:40
| valign="center" |11:05
| valign="center" |1373
| valign="center" |2
| valign="center" |16:25
| valign="center" |16:45
| valign="center" |448
| valign="center" |1
|-
| valign="center" |UMB
| valign="center" |अम्बाला कैंट जंक्शन
| valign="center" |14:40
| valign="center" |14:55
| valign="center" |1572
| valign="center" |2
| valign="center" |12:35
| valign="center" |13:05
| valign="center" |250
| valign="center" |1
|-
| valign="center" |ASR
| valign="center" |अमृतसर
| valign="center" |19:20
| valign="center" |गंतव्य
| valign="center" |1821
| valign="center" |2
| valign="center" |स्रोत
| valign="center" |08:10
| valign="center" |0
| valign="center" |1
|}
==संदर्भ==
{{reflist}}
[[श्रेणी:भारतीय रेल के गाड़ी]]
[[श्रेणी:भारतीय रेल]]
6c6llfmfrsqoh1rn26u6r6z37wlh0hm
पितरपख
0
49553
803384
697990
2026-07-29T17:49:35Z
SM7
3953
+विकिकड़ी जोड़ल गइल
803384
wikitext
text/x-wiki
{{Infobox holiday
|holiday_name = पितरपख
|type = हिंदू
|image = Pitru Paksha 2007.jpg
|caption = बाणगंगा पोखरा के किनारे पितरपख के नहान आ जल दिहल, 7 सितंबर 2007
|observedby = [[हिंदू]]
|begins = भादो के पुर्नवासी
|ends = कुआर के अमौसा
|date = सितंबर/अक्टूबर
|date2016 = सितंबर 16 – अक्टूबर 1 <ref>{{cite web|url=http://www.drikpanchang.com/vrats/shraddhadates.html|title=2016 Shraddha Days|publisher=दृक पञ्चांग}}</ref>
|date2017 = सितंबर 5-19
|celebrations = 16 [[तिथी|चंद्रमा तिथी]]
|observances = श्राद्ध
|relatedto = पितर (पूर्वज) लोग के तर्पण
}}
'''पितरपख''' (संस्कृत: पितृपक्ष) [[कुआर]] महीना के [[अन्हार पाख|अन्हरिया वाला पाख]] के कहल जाला जे [[भादो]] के [[पुर्नवासी]] के अगिला दिन से सुरू हो के कुआर के [[अमौसा]] ले, कुल सोरह दिन, रहे ला आ एह समय के दौरान [[हिंदू|हिंदू लोग]] अपना पितर लोग के तिथी मनावे ला आ सालाना श्राद्ध करे ला। एकरा के '''पितृपक्ष''', आ '''सोरह श्राद्ध''' के नाँव से भी जानल जाला।
[[File:Tarpan - Jagannath Ghat - Kolkata 2012-10-15 0648.JPG|thumb|जगन्नाथ घाट हुगली नदी के किनारे पितरपख के नहान, कोलकाता 2012-10-15]]
एह अवधि के हिंदू लोग अशुभ माने ला, काहें से की एह में मौत आ मर चुकल लोग के पूजा (श्राद्ध) के बिधान हवे। एही कारण कई तरह के शुभ कामकाज एह दौरान बंद रहे ला। एकरे अंतिम तिथी के भोजपुरी क्षेत्र में '''पितरसउनन''' कहल जाला, जबकि अन्य क्षेत्रन में सर्वपित्री अमावस्या, पितृ अमावस्या, पेद्दल अमावस, महालय अमावस्या भा महालय कहल जाला। महालय से बंगाल में [[दुर्गा पूजा]] के सुरुआत मानल जाला। अगिला दिन से [[नवरातर]] के सुरुआत हो जाला।
{{clear}}
==संदर्भ==
{{Reflist|35em}}
{{हिंदू तिहुआर}}
[[श्रेणी:हिंदू तिहुआर]]
[[श्रेणी:कुआर महीना के तिहुआर]]
{{hinduism-stub}}
4p490wrd4w6omxpw64t21nfl9d506yq
गौतम अडानी
0
50477
803364
801917
2026-07-29T16:23:53Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803364
wikitext
text/x-wiki
{{Infobox person
| name = गौतम अडानी
| image = Gautam Adani.jpg
| alt =
| caption =
| birth_name = गौतम शांतिलाल अडानी
| birth_date = {{Birth date and age|df=yes|1962|06|24}}
| birth_place = [[अहमदाबाद]], गुजरात, भारत
| residence = अहमदाबाद, गुजरात, भारत
| death_date = <!-- {{Death date and age|df=yes|YYYY|MM|DD|YYYY|MM|DD}} or {{Death-date and age|Month DD, YYYY|Month DD, YYYY}} (death date then birth date) -->
| death_place =
| nationality = [[भारत के लोग|भारतीय]]
| other_names =
|networth = $8.81 बिलियन (सितंबर 2017)<ref name="Forbes">{{cite web|title=Gautam Adani & family|url=https://www.forbes.com/profile/gautam-adani/|website=Forbes|accessdate=6 अप्रैल 2017}}</ref>
| known_for = संस्थापक आ चेयरमैन, अडानी ग्रुप
| occupation = बिजनेसमैन
| parents = शांतिलाल अडानी<br/>शांता अडानी
| spouse = प्रीती अडानी
| children = करन<br/>जीत
| relatives = बिनोद अडानी (भाई)
}}
'''गौतम शांतिलाल अडानी''' (जनम 24 जून 1962) एगो भारतीय अरबपति उद्योगपति आ फिलेंथ्रोपिस्ट (परोपकारी, मानवताप्रेमी) बाड़ें।<ref>{{Cite news|title=Gautam Adani, Nita Ambani & KM Birla top philanthropic leaders in India|work=The Economic Times|url=https://economictimes.indiatimes.com/magazines/panache/gautam-adani-nita-ambani-km-birla-top-philanthropic-leaders-in-india/articleshow/85291662.cms|access-date=2022-01-15}}</ref> ऊ भारत में बंदरगाह बिकास आ संचालन से जुड़ल अहमदाबाद के बहुराष्ट्रीय समूह [[अडानी ग्रुप]] के [[अध्यक्ष]] आऊर संस्थापक बाड़ें।<ref name="Bloomberg">{{cite news|title= Gautam Adani Bloomberg Index |newspaper= Bloomberg.com |url= https://www.bloomberg.com/billionaires/profiles/gautam-s-adani/ |access-date=11 February 2022}}</ref> अडानी [[अडानी फाउंडेशन]] के अध्यक्ष भी बाड़े, जवना के नेतृत्व मुख्य रूप से उनुकर पत्नी [[प्रीति अडानी]] करेली।
==सुरुआती जिनगी==
अडानी के जनम 24 जून 1962 के [[गुजरात]] के बनिया परिवार में शांतिलाल आ शांति अडानी के घरे गुजरात के [[अहमदाबाद]] में भइल।<ref>{{cite news|url=http://www.businessworld.in/amp/article/The-Rise-Of-The-Tycoon/05-02-2018-139609/|title=The Rise Of The Tycoon: Gautam Adani|website=[[Businessworld]]|first=Himani|last=Chandna|date=2019-02-26|access-date=2022-06-24|archive-date=2022-03-17|archive-url=https://web.archive.org/web/20220317122500/https://www.businessworld.in/amp/article/The-Rise-Of-The-Tycoon/05-02-2018-139609/|url-status=dead}}</ref> इनके 7 गो भाई-बहिन बाड़ें आ इनके माता-पिता गुजरात के उत्तरी हिस्सा के थराद कस्बा से पलायन कइले रहलें।<ref>{{cite news |title=A look into profile of billionaire Gautam Adani |url=https://www.indiatoday.in/magazine/profile/story/20110829-profile-of-billionaire-gautam-adani-747307-2011-08-20 |access-date=23 May 2022 |work=India Today |date=20 August 2011 |language=en}}</ref> इनके पिता एगो छोट कपड़ा के व्यापारी रहलें।<ref>{{cite news |url=https://www.indiatvnews.com/business/india/top-10-gujarati-billionaires-3732.html?page=4 |title= Top 10 Gujrati Billionaires |website= [[India TV]] |date= 2015-08-01}}</ref> इनके पढ़ाई अहमदाबाद के शेठ चिमनलाल नागींदास विद्यालय में भइल। गुजरात विश्वविद्यालय में वाणिज्य में स्नातक के डिग्री खातिर नामांकन लिहलें, बाकी दुसरा साल के बाद पढ़ाई छोड़ दिहलें।<ref name="gujarati">{{cite news |last1=Joseph |first1=Anto T. |title=Gautam Adani: Another Gujarati who made it big |url=https://economictimes.indiatimes.com/news/company/corporate-trends/gautam-adani-another-gujarati-who-made-it-big/articleshow/2605913.cms |access-date=23 May 2022 |work=The Economic Times |date=8 December 2007}}</ref> अडानी के बिजनेस में इंटरेस्ट रहे, लेकिन बाबूजी के कपड़ा के बिजनेस में ना।<ref>{{cite journal |url= https://www.cambridge.org/core/journals/business-history-review/article/transnational-indian-business-in-the-twentieth-century/6B6236551634D106A0E65880574D7373/core-reader# |title= Transnational Indian Business in the Twentieth Century |author= Cambridge Core |journal= Business History Review |date= 2017-12-12|volume= 91 |issue= 4 |pages= 651–679 |doi= 10.1017/S0007680517001350 |s2cid= 158483062 }}</ref>
==कैरियर==
[[File:Secretary Kerry greets Executive Chairman Adani before meeting with Indian CEOs amid Vibrant Gujarat Summit.jpg|right|thumb|अमेरिका के सेक्रेटरी ऑफ़ स्टेट जॉन कैरी अडानी के स्वागत करत, 2014]]
किशोरावस्था में अडानी महेंद्र ब्रदर्स खातिर हीरा सॉर्टर के काम करे खातिर 1978 में [[मुंबई]] आ गइलें।<ref>{{cite journal|url=https://www.cambridge.org/core/journals/business-history-review/article/transnational-indian-business-in-the-twentieth-century/6B6236551634D106A0E65880574D7373/core-reader#|title= Transnational Indian Business in the Twentieth Century|date=2017-12-12|journal= Business History Review|volume=91|issue=4|pages=651–679|doi=10.1017/S0007680517001350|last1=Tumbe|first1=Chinmay|s2cid=158483062}}
</ref>
1981 में उनुकर बड़ भाई महासुखभाई अडानी अहमदाबाद में एगो प्लास्टिक यूनिट खरीदले अउरी उनुका के संचालन के प्रबंधन करे के नेवता देले। ई उद्यम पॉलीविनाइल क्लोराइड (पीवीसी) आयात के माध्यम से वैश्विक व्यापार के अडानी के प्रवेश द्वार निकलस।<ref name="gujarati"/>
1985 में ऊ छोट पैमाना पर उद्योगन खातिर प्राथमिक बहुलक के आयात शुरू कइलें 1988 में अडानी अडानी समूह के होल्डिंग कंपनी अडानी एक्सपोर्ट्स के स्थापना कइलें, जेकरा के अब अडानी इंटरप्राइजेज के नाँव से जानल जाला। मूल रूप से ई कंपनी कृषि आ बिजली के चीज सभ के कारोबार करत रहे।<ref name="gujarati"/>
1991 में आर्थिक उदारीकरण के नीति सभ उनके कंपनी खातिर अनुकूल निकलल आ ऊ बिजनेस सभ के बिस्तार धातु, कपड़ा आ कृषि उत्पाद सभ के बिजनेस में करे लगलें।<ref name="gujarati"/><ref>{{cite news |last1=Vora |first1=Rutam |title=Gautam Adani: Cementing his portfolio |url=https://www.thehindubusinessline.com/specials/corporate-file/gautam-adani-cementing-his-portfolio/article65439045.ece |access-date=23 May 2022 |work=www.thehindubusinessline.com |date=22 May 2022 |language=en}}</ref>
1994 में गुजरात सरकार मुंद्रा बंदरगाह के प्रबंधकीय आउटसोर्सिंग के घोषणा कइलस आ 1995 में अडानी के ई ठेका मिल गइल।<ref>{{cite news|url=https://timesofindia.indiatimes.com/news/Gautam-Adani-the-baron-to-watch-out-for-if-Narendra-Modi-becomes-king/articleshow/33521536.cms|title=Gautam Adani, the baron to watch out for if Narendra Modi becomes king|date=2014-04-10|work=[[The Times of India]]|first=Harit|last=Mehta|access-date=16 April 2022}}</ref>
1995 में उ पहिला जेटी के स्थापना कइले। मूल रूप से मुंद्रा पोर्ट एंड स्पेशल इकोनॉमिक जोन के ओर से संचालित इ संचालन अडानी पोर्ट्स एंड सेज (एपीएसईजेड) के हस्तांतरित क दिहल गइल। आज इ कंपनी सबसे बड़ निजी बहु बंदरगाह संचालक बा। मुंद्रा बंदरगाह भारत के सभसे बड़ निजी क्षेत्र के बंदरगाह बाटे, एकर क्षमता सालाना लगभग 21 करोड़ टन माल ढुलाई करे के क्षमता बाटे।<ref>{{Cite web|last=Manoj|first=P.|title=APSEZ set to become top container port operator|url=https://www.thehindubusinessline.com/economy/logistics/apsez-set-to-become-top-container-port-operator/article9801005.ece|access-date=2021-03-11|website=@businessline| date=3 August 2017 |language=en}}</ref><ref>{{cite news|title=Adani becomes first Indian port operator to record 200 MT cargo movement|work=The Economic Times|url=https://economictimes.indiatimes.com/industry/transportation/shipping-/-transport/adani-becomes-first-indian-port-operator-to-record-200-mt-cargo-movement/articleshow/68592301.cms?from=mdr|access-date=2021-03-11}}</ref>
1996 में अडानी समूह के पावर बिजनेस शाखा अडानी पावर के स्थापना अडानी कइले रहले। अडानी पावर के लगे 4620 मेगावाट के क्षमता वाला थर्मल पावर प्लांट बाड़ें, ई देश के सभसे बड़ निजी थर्मल पावर उत्पादक कंपनी हवे।<ref>{{cite news|url=https://economictimes.indiatimes.com/industry/energy/power/abb-links-648-mw-solar-project-at-kamuthi-with-national-grid/articleshow/52728908.cms|title=Adani Solar Project|date=2016-06-13|publisher=economictimes.com}}</ref>
2006 में अडानी बिजली उत्पादन के कारोबार में उतरले। साल 2009 से 2012 ले ऊ ऑस्ट्रेलिया के एबट प्वाइंट पोर्ट आ क्वींसलैंड के कारमाइकल कोयला खदान के अधिग्रहण कइलें।<ref>{{cite news|url=https://www.outlookbusiness.com/specials/the-boss/gautam-adani-1237|title=Gautam Adani, chairman Adani group|date=2015-07-10|publisher=outlookbusiness.com|access-date=2022-06-24|archive-date=2021-03-07|archive-url=https://web.archive.org/web/20210307025714/https://outlookbusiness.com/specials/the-boss/gautam-adani-1237|url-status=dead}}</ref>
मई 2020 में अडानी भारत के सौर ऊर्जा निगम (एसईसीआई) के ओर से 6 अरब अमेरिकी डॉलर के दुनिया के सबसे बड़ सौर बोली जीतले रहे। 8000 मेगावाट के फोटोवोल्टिक बिजली संयंत्र परियोजना के अडानी ग्रीन ले लीहें; अडानी सोलर 2000 मेगावाट के अतिरिक्त सोलर सेल आ मॉड्यूल निर्माण क्षमता के स्थापना करी।<ref>{{cite news|last=Reporter|first=S. I.|date=2020-06-09|title=Adani Green hits new high on winning world's largest solar bid worth $7 bn|work=Business Standard India|url=https://www.business-standard.com/article/markets/adani-green-hits-new-high-stock-zooms-127-in-three-months-120060900281_1.html|access-date=2020-06-10}}</ref><ref>{{Cite web|date=2020-06-09|title=Adani wins world's largest solar project; to invest Rs 45,000 crore|url=https://www.financialexpress.com/industry/adani-wins-worlds-largest-solar-project-to-invest-rs-45000-crore/1986420/|access-date=2020-06-10|website=The Financial Express|language=en-US}}</ref>
सितंबर 2020 में अडानी मुंबई अंतर्राष्ट्रीय हवाई अड्डा में 74% हिस्सेदारी हासिल कइलें, ई दिल्ली के बाद भारत के दुसरा सभसे व्यस्त हवाई अड्डा हवे।<ref>{{Cite web|last=Karmali|first=Naazneen|title=India's 10 Richest Billionaires 2021|url=https://www.forbes.com/sites/naazneenkarmali/2021/04/06/indias-10-richest-billionaires-2021/|access-date=2021-07-01|website=Forbes|language=en}}</ref>
फरवरी 2022 में ऊ एशिया के सभसे धनी ब्यक्ति बनलें आ मुकेश अंबानी के पीछे छोड़ दिहलें।<ref name="forbes1">{{cite web |last=Pratap |first=Aayushi |date=3 February 2022 |title=Indian Billionaire Gautam Adani Becomes Asia's Richest Person |url=https://www.forbes.com/sites/aayushipratap/2022/02/03/indian-billionaire-gautam-adani-becomes-asias-richest-person/ |access-date=11 February 2022 |work=Forbes}}</ref>
मई 2022 में अडानी परिवार अम्बुजा सीमेंट्स आ ओकर सहायक कंपनी एसीसी के होल्सिम ग्रुप से 10.5 बिलियन डॉलर में खरीद लिहलस, एगो बिदेसी स्पेशल-पर्पस इकाई के माध्यम से।<ref>{{Cite web|url=https://timesofindia.indiatimes.com/business/india-business/adani-to-acquire-holcim-india-assets-for-10-5-bn/articleshow/91581073.cms|title=holcim: Adani to acquire Holcim India assets for $10.5 bn - Times of India|website=The Times of India}}</ref>
==निजी जिनगी==
गौतम अडानी के बियाह प्रीति से भइल बा, इनहन लोग के दू गो संतान बाड़ें।<ref>{{cite web|url=https://business.mapsofindia.com/business-leaders/gautam-adani.html|title=Gautam Adani |date=2015-06-02|website=mapsofindia.com}}</ref><ref>{{cite news|url=https://ahmedabadmirror.indiatimes.com/columns/the-informer/achtung-baby/articleshow/53066653.cms|title=Priti Adani| date=2016-07-07|website=Ahmedabad mirror}}</ref> ऊ जैन इंटरनेशनल ट्रेड ऑर्गेनाइजेशन (JITO) के सदस्य बाड़ें<ref>{{Cite web | url=https://mumbaimirror.indiatimes.com/mumbai/other/jains-to-organise-trade-fair/articleshow/15652322.cms | title=Jains to organise trade fair|work=Mumbai Mirror|date=10 November 2006|access-date=14 May 2022}}</ref> इनके अपहरण क के 1998 में फिरौती खातिर बंधक बना के रखल गइल, बाकी बिना पइसा लिहले रिहा क दिहल गइल।<ref>{{cite news |title=Throwback: When India's fifth richest man was kidnapped for ransom |url=https://www.timesnownews.com/business-economy/companies/article/throwback-when-indias-fifth-richest-man-survived-a-kidnapping-and-terrorist-attack/606122 |access-date=13 December 2020 |work=Times Now News |date=14 June 2020 |language= en }}</ref><ref>{{cite news |title=Two accused of abducting Gautam Adani 20 years ago acquitted |url=https://indianexpress.com/article/cities/ahmedabad/two-accused-of-abducting-gautam-adani-20-years-ago-acquitted-5473592/ |access-date=13 December 2020 |work=The Indian Express |date=1 December 2018 |location=Ahmedabad |language=en}}</ref> उ 2008 के मुंबई हमला के दौरान ताज होटल में रहले।<ref>{{cite news |last1=Sazonov |first2=Bhuma |last2=Shrivastava |first3=P R |last3=Sanjai |first1=Alexander |title=Billionaire Survivor of Ransom, Terror Attacks Now Rivals Ambani |url=https://www.bloomberg.com/news/articles/2020-12-13/billionaire-survivor-of-ransom-terror-attacks-now-rivals-ambani |access-date=13 December 2020 |work=Bloomberg.com |date=13 December 2020 |language=en}}</ref><ref>{{cite news |last1=Shukla |first1=Nimish |title=Gautam Adani felt safe in Taj toilet |url=https://timesofindia.indiatimes.com/Gautam-Adani-felt-safe-in-Taj-toilet/articleshow/3767246.cms |access-date=13 December 2020 |work=The Times of India |date=28 November 2008 |language=en}}</ref>
== बिबाद ==
=== स्टॉक में हेरफेर अउरी लेखा धोखाधड़ी के आरोप ===
जनवरी 2023 में न्यूयॉर्क शहर के [[हिंडेनबर्ग रिसर्च]] दू साल के जांच के निष्कर्ष प्रकाशित कइलस जेह में दावा कइल गइल कि अडानी बाजार में हेरफेर आ लेखा कदाचार में शामिल रहलें; हिंडेनबर्ग इहो खुलासा कइलस कि ऊ अडानी ग्रुप के कंपनी सभ पर शार्ट पोजीशन रखले बा।<ref>{{cite news |last1=Thomas |first1=Chris |last2=Kalra |first2=Aditya |title=Hindenburg shorts India's Adani citing debt, accounting concerns; shares plunge |url=https://www.reuters.com/markets/asia/hindenburg-says-holds-short-positions-indias-adani-flags-risks-2023-01-25/ |access-date=25 January 2023 |work=[[रायटर्स]] |date=25 January 2023 |language=en}}</ref><ref>{{cite news |last1=Sorkin |first1=Andrew Ross |last2=Mattu |first2=Ravi |last3=Warner |first3=Bernhard |last4=Kessler |first4=Sarah |last5=Merced |first5=Michael J. de la |last6=Hirsch |first6=Lauren |last7=Livni |first7=Ephrat |title=A Short Seller Takes Aim at an Indian Corporate Giant |url=https://www.nytimes.com/2023/01/25/business/dealbook/short-seller-hindenburg-adani.html |access-date=25 January 2023 |work=[[द न्यूयार्क टाइम्स]] |date=25 January 2023}}</ref><ref>{{cite news |last1=Lockett |first1=Hudson |title=Adani shares take $10.8bn hit after Hindenburg bets against group |url=https://www.ft.com/content/4aedaaf9-9ee8-4334-945c-12ac08429d6a |access-date=25 January 2023 |work=[[फ़ाइनान्शियल टाइम्स]] |date=25 January 2023}}</ref> आरोप के बाद अडानी से जुड़ल कंपनी सभ के बांड आ शेयर सभ के कीमत में गिरावट आइल आ क्रेडिट सुइस अडानी ग्रुप के बांड सभ के जमानत के रूप में स्वीकार कइल बंद क दिहलस।<ref>{{cite news |last1=Lee |first1=Jihye |title=Adani shares fall as short seller firm Hindenburg announces short position |url=https://www.cnbc.com/2023/01/25/adani-group-hindenburg-short-position.html |access-date=25 January 2023 |work=[[CNBC]] |date=25 January 2023 |language=en}}</ref><ref>{{cite news |last1=Kay |first1=Chris |last2=Vishnoi |first2=Abhishek |last3=Joshi |first3=Ashutosh |title=Hindenburg Targets Asia’s Richest Man, Triggering Adani Selloff |url=https://www.bloomberg.com/news/articles/2023-01-25/adani-group-stocks-drop-after-hindenburg-takes-short-position |access-date=25 January 2023 |work=[[Bloomberg News]] |date=25 January 2023 |language=en}}</ref><ref>https://www.npr.org/2023/02/02/1153420761/adani-group-hindenburg</ref> अडानी धोखाधड़ी के आरोप सभ के बेबुनियाद आ बुरा इरादा बता के इनकार कइलें आ इनहन के "दुर्भावनापूर्ण" आ "चयनात्मक गलत जानकारी" बतवलें।<ref name="madhok-2023">
{{cite news
| last1 = Madhok | first1 = Diksha
| title = Asia's richest man slams short-seller's fraud claims as 'baseless' and 'malicious'
| date = 25 January 2023
| language = en
| work = CNN Business
| location = Atlanta, Georgia, USA
| url = https://www.cnn.com/2023/01/25/business/india-adani-shares-plunge-hindenburg-report-hnk-intl/index.html
| access-date = 25 January 2023
}}
</ref><ref>{{Cite news |date=2023-01-26 |title=Adani Group: Asia's richest man hits back at 'con' allegations |language=en-GB |work=BBC News |url=https://www.bbc.com/news/business-64408720 |access-date=2023-01-28}}</ref> एकरे बाद रिपोर्ट के तीन दिन बाद अडानी द्वारा नियंत्रित सूचीबद्ध कंपनी सभ के शेयर सभ के बजार कीमत में 48 बिलियन डॉलर के नुकसान भइल।<ref>{{Cite web |title=Gautam Adani: One of the world's richest men loses ground after £39bn stock market slump |url=https://news.sky.com/story/gautam-adani-one-of-the-worlds-richest-men-loses-ground-after-39bn-stock-market-slump-12796545 |access-date=2023-01-28 |website=स्काई न्यूज |language=en}}</ref>
==फिलेंथ्रोपिस्ट के रूप में==
अडानी अडानी फाउंडेशन के अध्यक्ष बाड़े, जवना के फंडिंग अडानी ग्रुप के माध्यम से कइल जाला। एकर स्थापना 1996 में भइल।गुजरात के अलावा ई फाउंडेशन [[महाराष्ट्र]], [[राजस्थान]], हिमाचल प्रदेश, [[मध्य प्रदेश]], छत्तीसगढ़, आ ओडिशा राज्य सभ में काम करे ला।<ref>{{Cite web|title=Adani Foundation {{!}} Youth4work|url=https://www.university.youth4work.com/adani-foundation|access-date=2020-12-07|website=youth4work.com|language=en|archive-date=2022-03-31|archive-url=https://web.archive.org/web/20220331015615/https://www.university.youth4work.com/adani-foundation|url-status=dead}}</ref>
मार्च 2020 में, ऊ अपना समूह के परोपकार शाखा के माध्यम से पीएम केयर्स फंड में ₹100 करोड़ (13 मिलियन अमेरिकी डॉलर) के योगदान दिहलें, कोरोना वायरस के प्रकोप से लड़े खातिर।<ref>{{Cite web|url=https://www.indiatvnews.com/business/news-coronavirus-gautam-adani-donation-rs-1000-crore-pm-fund-602679|title=Gautam Adani gives Rs 100 crore to PM Fund to fight coronavirus|last=Joshi|first=Manas|date=2020-03-29|website=www.indiatvnews.com|language=en|access-date=2020-04-01}}</ref> गुजरात सीएम राहत कोष में ₹5 करोड़ (660,000 अमेरिकी डॉलर) आ महाराष्ट्र सीएम राहत कोष में ₹1 करोड़ (130,000 अमेरिकी डॉलर) के योगदान दिहल गइल।<ref>{{cite news|url=https://economictimes.indiatimes.com/news/company/corporate-trends/billionaire-gautam-adani-commits-to-support-fight-against-coronavirus/articleshow/74983932.cms|title=Billionaire Gautam Adani commits to support fight against coronavirus|date=2020-04-04|work=The Economic Times|access-date=2020-04-10}}</ref>
अडानी के नेतृत्व में विविध समूह अडानी समूह सऊदी अरब के दम्माम से गुजरात के मुंड्रा में 80 मीट्रिक टन तरल मेडिकल ऑक्सीजन से भरल चार आईएसओ क्रायोजेनिक टैंक के आयात कइलस। संगही, इ समूह लिंडे सऊदी अरब से 5000 मेडिकल ग्रेड के ऑक्सीजन सिलेंडर भी हासिल कइले बिया। एगो ट्विटर पोस्ट में अडानी साझा कइले कि, उनुकर समूह गुजरात के कच्छ जिला में जहां-जहां जरूरत बा, उहाँ हरेक दिन उनुकर ग्रुप 1500 सिलेंडर के मेडिकल ऑक्सीजन के सप्लाई करता।<ref>{{Cite web|last=My Nation|title=To improve oxygen availability, Adani group imports cryogenic tanks from Saudi Arabia|url=https://www.mynation.com/india-news/to-improve-oxygen-availability-adani-group-imports-cryogenic-tanks-from-saudi-arabia-qs47sk|url-status=live|access-date=2021-05-11|website=Asianet News Network Pvt Ltd|language=en}}</ref>
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:जियत लोग]]
[[श्रेणी:1964 में जनम]]
[[श्रेणी:भारतीय बिजनेसमैन]]
a8j3ra9vjw8z8wgnus8h3qv3gtiqu49
आटा
0
60493
803289
786883
2026-07-29T16:06:02Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803289
wikitext
text/x-wiki
[[File:All-Purpose Flour (4107895947).jpg|thumb|आटा]]
'''आटा''' चाहे '''पिसान''' कई किसिम क [[अनाज]], [[फल]] या [[जड़]] के महिन बुरादा (पाउडर) ह। अनाज, फल या जड़ के [[मील]] या [[जाँता]] या [[ओखर]] में पीस/कूट के आटा प्राप्त कइल जाला। आटा से [[रोटी]] आ विभिन्न प्रकार के व्यंजन (खाये वाली चीज) बनावल जाला। <ref>{{cite web |url= http://www.hindi2dictionary.com/%E0%A4%86%E0%A4%9F%E0%A4%BE-meaning-hindi.html |title= आटा meaning in hindi |date= |website= www.hindi2dictionary.com |publisher= |access-date= 17 May 2018 |archive-date= 28 April 2018 |archive-url= https://web.archive.org/web/20180428214010/http://www.hindi2dictionary.com/%E0%A4%86%E0%A4%9F%E0%A4%BE-meaning-hindi.html |url-status= dead }}</ref> [[अन्न]] के आटा से रोटी आ ब्रेड तैयार कइल जाऽला, जवन हमनी के रोज के खाना में शामिल बा। [[गेंहू के आटा]] [[ओशिनिया]], [[यूरोप]], [[दक्खिन अमेरिका]], [[उत्तर अमेरिका]], [[मध्य पूरुब]], [[उत्तर भारत]] आ [[उत्तर अफ्रीका]] के संस्कृति में पावल जाला, जहां लोग अपन-अपन संस्कृति के हिसाब से रोटी आ पेस्ट्री (आटा से बनल मिठाई) बनावेला लोग।
==आटा के कुछ प्रकार==
* गेंहू के आटा (wheat flour) अनाज से बनेला।
* चावल के आटा (rice flour) अनाज से बनेला
* [[बेसन]] चाहे चना क आटा
* मकई के आटा (corn flour) अनाज से
* नारियल के आटा (coconut flour) फल से
* केरा के आटा (banana flour) फल से
* कसावा के आटा (cassava flour) जड़ से बनेला
==संदर्भ==
{{Reflist}}
==बाहरी लिंक==
* [http://www.thisisinsider.com/how-bread-is-eaten-in-27-countries-around-the-world-2016-10 How bread is eaten in 27 countries around the world]
[[श्रेणी:खाना बनावे में इस्तमाल होखे वाला सामग्री]]
{{food-stub}}
bhas9n7nwn8u2mbjjmvkexs70axxx0j
पहिला बिस्व जुद्ध
0
65676
803310
790815
2026-07-29T16:19:58Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803310
wikitext
text/x-wiki
{{Infobox military conflict
| conflict = [[World War I]]
| image = WWImontage.jpg
| image_size = 300px
| caption = '''Clockwise from the top''': The aftermath of shelling during the [[Battle of the Somme]], [[Mark V tank]]s cross the [[Hindenburg Line]], {{HMS|Irresistible|1898|6}} sinks after hitting a [[naval mine|mine]] in the [[Naval operations in the Dardanelles Campaign|Dardanelles]], a British [[Vickers machine gun]] crew wears [[gas mask]]s during the Battle of the Somme, [[Albatros D.III]] fighters of [[Jagdstaffel 11]]
| date = {{start and end dates|1914|7|28|1918|11|11|df=yes}}<br />({{Age in years, months, weeks and days|month1=7|day1=28|year1=1914|month2=11|day2=11|year2=1918}})
{{Collapsible list
| bullets = yes
| title = Peace treaties
|[[Treaty of Versailles]]<br />Signed 28 June 1919<br />({{Age in years, months, weeks and days|month1=7|day1=28|year1=1914|month2=6|day2=28|year2=1919}}){{efn|The United States did not ratify any of the treaties agreed to at the [[Paris Peace Conference, 1919|Paris Peace Conference]].}}<!--Since World War I didn't actually end for most states until the Paris Peace Conference treaties were signed...-->
|[[Treaty of Saint-Germain-en-Laye (1919)|Treaty of Saint-Germain-en-Laye]]<br />Signed 10 September 1919<br />({{Age in years, months, weeks and days|month1=7|day1=28|year1=1914|month2=9|day2=10|year2=1919}})
|[[Treaty of Neuilly-sur-Seine]]<br />Signed 27 November 1919<br />({{Age in years, months, weeks and days|month1=10|day1=14|year1=1915|month2=11|day2=27|year2=1919}}){{efn|Bulgaria joined the Central Powers on 14 October 1915.}}
|[[Treaty of Trianon]]<br />Signed 4 June 1920<br />({{Age in years, months, weeks and days|month1=7|day1=28|year1=1914|month2=6|day2=4|year2=1920}})
|[[Treaty of Sèvres]]<br />Signed 10 August 1920<br />({{Age in years, months, weeks and days|month1=7|day1=28|year1=1914|month2=8|day2=10|year2=1920}}){{efn|The Ottoman Empire agreed to a secret alliance with Germany on 2 August 1914. It joined the war on the side of the Central Powers on 29 October 1914.}}
|[[U.S.–Austrian Peace Treaty (1921)|United States–Austria Peace Treaty]]<br />Signed 24 August 1921<br />({{Age in years, months, weeks and days|month1=12|day1=7|year1=1917|month2=8|day2=24|year2=1921}}){{efn|The United States [[American entry into World War I#Austria-Hungary|declared war on Austria-Hungary]] on 7 December 1917.}}{{efn|[[First Republic of Austria|Austria]] was considered one of the [[successor states]] to Austria-Hungary.}}
|[[U.S.–German Peace Treaty (1921)|United States–Germany Peace Treaty]]<br />Signed 25 August 1921<br />({{Age in years, months, weeks and days|month1=4|day1=6|year1=1917|month2=8|day2=25|year2=1921}}){{efn|The United States [[United States declaration of war on Germany (1917)|declared war on Germany]] on 6 April 1917.}}
|[[U.S.–Hungarian Peace Treaty (1921)|United States–Hungary Peace Treaty]]<br />Signed 29 August 1921<br />({{Age in years, months, weeks and days|month1=12|day1=7|year1=1917|month2=8|day2=29|year2=1921}}){{efn|[[Kingdom of Hungary (1920–46)|Hungary]] was considered one of the successor states to Austria-Hungary.}}
|[[Treaty of Lausanne]]<br />Signed 24 July 1923<br />({{Age in years, months, weeks and days|month1=10|day1=29|year1=1914|month2=7|day2=24|year2=1923}}){{efn|Although the [[Treaty of Sèvres]] was intended to end the war between the Allied Powers and the Ottoman Empire, the Allied Powers and the [[Republic of Turkey]], the successor state of the Ottoman Empire, agreed to the Treaty of Lausanne.}}
}}
| place = Europe, Africa, the Middle East, the [[Pacific Islands]], China, Indian Ocean, and off the coast of South and North America
| casus = [[Assassination of Archduke Franz Ferdinand]] (28 June) followed by Austro-Hungarian declaration of war on [[Kingdom of Serbia]] (28 July) and Russian mobilisation against [[Austria-Hungary]] (29 July).
| territory =
* Formation of new countries in Europe and the Middle East
* Transfer of [[German colonies]] and [[Partitioning of the Ottoman Empire|regions of the former Ottoman Empire]] to other powers
| result = [[Allies of World War I|Allied Powers]] victory
* [[Central Powers]]' victory on the [[Eastern Front (World War I)|Eastern Front]] nullified by defeat on the [[Western Front (World War I)|Western Front]]
* Fall of all of the continental European ([[German Empire|German]], [[Russian Empire|Russian]], [[Ottoman Empire|Ottoman]], and [[Austria-Hungary|Austro-Hungarian]]) empires
* [[Russian Civil War]] and foundation of the [[Soviet Union]]
* Establishment of the [[League of Nations]] ([[World War I#Aftermath|more...]])
| combatant1 = '''[[Allies of World War I|Allied Powers]]:'''{{plainlist|
* {{flagcountry|French Third Republic}}
* {{flagcountry|British Empire}}
* {{flagcountry|Russian Empire}}<br />{{small|(1914–17)}}
* {{flagcountry|Kingdom of Serbia}}
* {{flag|Belgium}}
* {{flagcountry|Kingdom of Montenegro}}
* {{flagcountry|Empire of Japan}}
* {{flagcountry|Kingdom of Italy}}<br />{{small|(1915–18)}}
* {{flag|United States|1912}}<br />{{small|(1917–18)}}
* {{flagcountry|Kingdom of Romania}}<br />{{small|(1916–18)}}
* {{flagcountry|First Portuguese Republic}}<br />{{small|(1916–18)}}
* {{flagicon image|Flag of Hejaz (1917).svg}} [[Kingdom of Hejaz|Hejaz]]<br />{{small|(1916–18)}}
* {{flagicon image|Flag of China (1912–1928).svg}} [[Republic of China (1912–1949)|China]]<br />{{small|(1917–18)}}
* {{flagcountry|Kingdom of Greece}}<br />{{small|(1917–18)}}
* {{flagicon|Thailand}} [[Rattanakosin Kingdom (1782–1932)|Siam]]<br />{{small|(1917–18)}}
* {{small|[[Allies of World War I|...''and others'']]}}}}
| combatant2 = '''[[Central Powers]]:'''{{plainlist|
* {{flag|German Empire}}
* {{flag|Austria-Hungary}}
* {{flag|Ottoman Empire}}
* {{flagcountry|Kingdom of Bulgaria}}<br />{{small|(1915–18)}}
* {{small|[[Central Powers#Co-belligerents|...''and co-belligerents'']]}}}}
| commander1 = '''[[Allied leaders of World War I|Allied Powers leaders]]:'''{{plainlist|
* {{flagicon|French Third Republic}} [[Raymond Poincaré]]
* {{flagdeco|French Third Republic}} [[Georges Clemenceau]]
* {{flagicon|United Kingdom of Great Britain and Ireland|United Kingdom}} [[H. H. Asquith]]
* {{flagdeco|United Kingdom of Great Britain and Ireland|United Kingdom}} [[David Lloyd George]]
* {{flagicon|Belgium}} [[Albert I of Belgium|Albert I]]
* {{flagicon|Russian Empire}} [[Nicholas II of Russia|Nicholas II]]
* {{flagicon|Kingdom of Italy}} [[Victor Emmanuel III]]
* {{flagdeco|Kingdom of Italy}} [[Vittorio Orlando]]
* {{flagicon|United States|1912}} [[Woodrow Wilson]]
* {{flagicon|Empire of Japan}} [[Emperor Taishō|Yoshihito]]
* {{flagicon|Kingdom of Serbia}} [[Peter I of Serbia|Peter I]]
* {{flagicon|Kingdom of Romania}} [[Ferdinand I of Romania|Ferdinand I]]
* {{flagicon|Kingdom of Greece}} [[Eleftherios Venizelos]]
* {{small|[[Allied leaders of World War I|...''and others'']]}}}}
| commander2 = '''[[Leaders of the Central Powers of World War I|Central Powers leaders]]:'''{{plainlist|
* {{flagicon|German Empire}} [[Wilhelm II, German Emperor|Wilhelm II]]
* {{flagicon|Austria-Hungary}} [[Franz Joseph I]]{{KIA|Death by natural causes}}
* {{flagdeco|Austria-Hungary}} [[Charles I of Austria|Karl I]]
* {{flagicon|Ottoman Empire}} [[Mehmed V]]{{KIA|Death by natural causes}}
* {{flagdeco|Ottoman Empire}} [[Mehmed VI]]
* {{flagdeco|Ottoman Empire}} [[Three Pashas]]
* {{flagicon|Kingdom of Bulgaria}} [[Ferdinand I of Bulgaria|Ferdinand I]]
* {{small|[[Leaders of the Central Powers of World War I|...''and others'']]}}}}
| strength1 = {{plainlist|
* {{flagicon|Russian Empire}} 12,000,000
* {{flagicon|British Empire}} 8,841,541<ref>{{cite web|url=http://www.1914-1918.net/faq.htm|title=British Army statistics of the Great War|publisher=1914-1918.net|accessdate=13 December 2011|archive-date=2 May 2019|archive-url=https://web.archive.org/web/20190502090525/http://www.1914-1918.net/faq.htm|url-status=dead}}</ref><ref>Figures are for the British Empire</ref>
* {{flagicon|French Third Republic}} 8,660,000<ref>Figures are for Metropolitan France and its colonies</ref>
* {{flagicon|Kingdom of Italy}} 5,615,140
* {{flagicon|United States|1912}} 4,743,826
* {{flagicon|Kingdom of Romania}} 1,234,000
* {{flagicon|Empire of Japan}} 800,000
* {{flagicon|Kingdom of Serbia}} 707,343
* {{flagicon|Belgium}} 380,000
* {{flagicon|Kingdom of Greece}} 250,000
* {{Flagicon|First Portuguese Republic}} 80,000
* {{flagicon|Kingdom of Montenegro}} 50,000
}}
''Total: 42,959,850''<ref name="Tucker 2005 273">{{harvnb|Tucker|Roberts|2005|p=273}}</ref>
| strength2 = {{plainlist|
* {{flagicon|German Empire}} 13,250,000
* {{flagicon|Austria-Hungary}} 7,800,000
* {{flagicon|Ottoman Empire}} 2,998,321
* {{flagicon|Kingdom of Bulgaria}} 1,200,000}}
''Total: 25,248,321''<ref name="Tucker 2005 273"/>
| casualties1 = {{plainlist|
* '''Military dead:''' 5,525,000
* '''Military wounded:''' 12,831,500
* '''Total:''' 18,356,500 KIA, WIA and MIA
* '''Civilian dead:''' 4,000,000}}
{{small|[[World War I casualties|...''further details''.]]}}
----
{{flagicon|French Third Republic}} 1,397,800 killed<ref name="crb.org"/><br />
{{flagicon|British Empire}} 1,114,914 killed<ref name="crb.org">{{Cite web |url=http://www.centre-robert-schuman.org/userfiles/files/REPERES%20%E2%80%93%20module%201-1-1%20-%20explanatory%20notes%20%E2%80%93%20World%20War%20I%20casualties%20%E2%80%93%20EN.pdf |title=आर्काइव कॉपी के बा |access-date=2018-10-19 |archive-date=2021-02-02 |archive-url=https://web.archive.org/web/20210202015715/http://www.centre-robert-schuman.org/userfiles/files/REPERES%20%E2%80%93%20module%201-1-1%20-%20explanatory%20notes%20%E2%80%93%20World%20War%20I%20casualties%20%E2%80%93%20EN.pdf |url-status=dead }}</ref><br />
{{flagicon|Kingdom of Italy}} 651,000 killed<ref name="crb.org"/><br />
{{flagicon|Russian Empire}} 1,811,000 killed<ref name="crb.org"/> <br />
{{flagicon|Kingdom of Romania}} 250,000<ref name="crb.org"/>-335,000 killed<ref name="Nash">{{cite book|last1=Nash|title=Darkest Hours|date=1976|publisher=Rowman & Littlefield|url=https://books.google.com/books?id=3KglCgAAQBAJ&pg=PA775#v=onepage&q&f=false|isbn=9781590775264}}</ref><br />
{{Flagicon|Empire of Japan}} 415 killed<ref name="crb.org"/><br/>
{{Flagicon|Kingdom of Serbia}} 275,000 killed<ref name="crb.org"/><br/>
{{flagicon|Belgium}} 58,637<ref name="crb.org"/>-87,500 killed<ref name="Nash" /><br />{{flagicon|United States|1912}} 116,708 killed<ref name="crb.org"/><br/>
{{Flagicon|Kingdom of Greece}} 26,000 killed<ref name="crb.org"/><br/>
{{Flagicon|First Portuguese Republic}} 7,222 killed<ref name="crb.org"/><br/>
{{Flagicon|Kingdom of Montenegro}} 3,000 killed<ref name="crb.org"/>
| casualties2 = {{plainlist|
* '''Military dead:''' 4,386,000
* '''Military wounded:''' 8,388,000
* '''Total:''' 12,774,000 KIA, WIA and MIA
* '''Civilian dead:''' 3,700,000}}
{{small|[[World War I casualties|...''further details''.]]}}
----
{{flagicon|German Empire}} 2,050,897 killed<ref name="crb.org"/><br />{{flagicon|Austria-Hungary}} 1,200,000 killed<ref name="Nash" /><br />{{flagicon|Ottoman Empire}} 771,844 killed<ref name="crb.org"/><br/>{{flagicon|Kingdom of Bulgaria}} 87,500 killed<ref name="crb.org"/>
| campaignbox = {{Campaignbox World War I}}
}}
'''पहिला बिस्व जुद्ध''' (World War I) 28 जुलाई 1914 से 11 नवंबर 1918 ले चलल रहे। ई जुद्ध आज तक के सभसे बड़ जुद्धन मे ले एगो बाटे।
{{clear}}
==नोट==
<references group="नोट"/>
== नाँव ==
''विश्व युद्ध'' शब्द के पहिला बेर सितंबर १९१४ में जर्मन जीवविज्ञानी अउरी दार्शनिक अर्नस्ट हेकेल द्वारा गढ़ल गइल रहे। उ दावा कइले कि, "एह में कवनो संदेह नइखे कि आशंकित 'यूरोपीय युद्ध' के रस्ता अउरी चरित्र।" ... शब्द के पूरा अर्थ में पहिला बिस्व जुद्ध बन जाई," {{Sfn|Shapiro|Epstein|2006|p=329}} २० सितंबर १९१४ के ''द इंडियानापोलिस स्टार'' में।
'''पहिला बिस्व जुद्ध''' शब्द (अक्सर संक्षिप्त रूप से '''WWI''' या '''WW1''' ), लेफ्टिनेंट कर्नल द्वारा इस्तेमाल कइल गइल रहे। चार्ल्स à कोर्ट रेपिंगटन, उनके संस्मरण सभ खातिर टाइटिल के रूप में (१९२० में प्रकाशित); ऊ १० सितंबर १९१८ के अपना डायरी एंट्री में हार्वर्ड विश्वविद्यालय के एगो मेजर जॉनस्टोन के साथे एह मामिला पर आपन चर्चा के नोट कइले रहलें <ref>{{Cite news|url=https://public.oed.com/blog/june-2014-update-chief-editors-notes-june-2014/|title=Chief Editor's notes June 2014|last=Proffitt|first=Michael|date=2014-06-13|work=Oxford English Dictionary's blog}}</ref> [[दुसरा बिस्व जुद्ध|द्वितीय विश्वयुद्ध]] से पहिले १९१४–१९१८ के घटना के आम तौर पर '''महायुद्ध''' या खाली '''विश्वयुद्ध के''' नाम से जानल जात रहे .<ref>{{Cite web|url=http://www.history.com/news/ask-history/were-they-always-called-world-war-i-and-world-war-ii|title=Were they always called World War I and World War II?|website=Ask History|archive-url=https://web.archive.org/web/20151001001745/http://www.history.com/news/ask-history/were-they-always-called-world-war-i-and-world-war-ii|archive-date=1 October 2015|access-date=24 October 2013}}</ref> {{Sfn|Braybon|2004|p=8}} अगस्त १९१४ में ''द इंडिपेंडेंट'' पत्रिका "ई महान युद्ध ह" लिखलस। ई आपन नाँव रखे ला"। <ref name="independent19140817">{{Cite magazine |date=1914-08-17 |title=The Great War |url=https://archive.org/details/independen79v80newy/page/n233/mode/1up?view=theater |magazine=The Independent |page=228 |access-date=2022-05-17}}<cite class="citation magazine cs1 cs1-prop-jul-greg-uncertainty" data-ve-ignore="true">[https://archive.org/details/independen79v80newy/page/n233/mode/1up?view=theater "The Great War"]. ''The Independent''. 17 August 1914. p. 228<span class="reference-accessdate">. Retrieved <span class="nowrap">17 May</span> 2022</span>.</cite>
</ref> अक्टूबर १९१४ में कनाडा ''के पत्रिका मैकलिन'' एही तरे लिखले रहे कि, "कुछ युद्ध अपना नाम के नाम लेला।" ई महायुद्ध हवे।" <ref>{{Cite web|url=http://www.oed.com/view/Entry/81104|title=great, adj., adv., and n|website=Oxford English Dictionary|url-access=subscription|archive-url=https://web.archive.org/web/20190514194006/https://www.oed.com/view/Entry/81104|archive-date=14 May 2019|access-date=19 March 2012}}<cite class="citation web cs1" data-ve-ignore="true"><span class="cs1-lock-subscription" title="Paid subscription required">[http://www.oed.com/view/Entry/81104 "great, adj., adv., and n"]</span>. ''Oxford English Dictionary''. [https://web.archive.org/web/20190514194006/https://www.oed.com/view/Entry/81104 Archived] from the original on 14 May 2019<span class="reference-accessdate">. Retrieved <span class="nowrap">19 March</span> 2012</span>.</cite></ref> समकालीन यूरोपीय लोग एकरा के " युद्ध खतम करे वाला लड़ाई " भी कहल आ एकरे तब के बेजोड़ पैमाना, तबाही आ जान के नुकसान के बारे में इनहन के धारणा के कारण एकरा के "सब युद्ध सभ के खतम करे वाला लड़ाई" भी बतावल गइल। <ref>{{Cite news|url=http://news.bbc.co.uk/2/hi/special_report/1998/10/98/world_war_i/198172.stm|title=The war to end all wars|date=10 November 1998|work=BBC News|access-date=15 December 2015|archive-url=https://web.archive.org/web/20150619035838/http://news.bbc.co.uk/2/hi/special_report/1998/10/98/world_war_i/198172.stm|archive-date=19 June 2015}}<cite class="citation news cs1" data-ve-ignore="true">[http://news.bbc.co.uk/2/hi/special_report/1998/10/98/world_war_i/198172.stm "The war to end all wars"]. ''BBC News''. 10 November 1998. [https://web.archive.org/web/20150619035838/http://news.bbc.co.uk/2/hi/special_report/1998/10/98/world_war_i/198172.stm Archived] from the original on 19 June 2015<span class="reference-accessdate">. Retrieved <span class="nowrap">15 December</span> 2015</span>.</cite></ref> विश्वयुद्ध के बाद{{Spaces}}II १९३९ में शुरू भइल, ई शब्द अउरी मानक हो गइलें, कनाडा के लोग समेत ब्रिटिश साम्राज्य के इतिहासकार लोग "पहिला बिस्व जुद्ध" आ अमेरिकी लोग "विश्वयुद्ध" के पक्ष में रहल{{Spaces}}I".<ref>Margery Fee and Janice McAlpine. ''Guide to Canadian English Usage''. (Oxford UP, 1997), p. 210.</ref>
== पृष्ठभूमि ==
{{Main|Causes of World War I}}
=== राजनीतिक आ सैन्य गठबंधन के बात कइल जाव ===
[[File:Map_Europe_alliances_1914-en.svg|alt=Map of Europe focusing on Austria-Hungary and marking the central location of ethnic groups in it including Slovaks, Czechs, Slovenes, Croats, Serbs, Romanians, Ukrainians, Poles.|thumb|१९१४ में प्रतिद्वंद्वी सैन्य गठबंधन: हरियर रंग में ट्रिपल एंटेन्टे ; भूरे रंग में ट्रिपल एलायंस के बा । खाली ट्रिपल एलायंस एगो औपचारिक "गठबंधन" रहे; बाकी लोग के सूचीबद्ध समर्थन के अनौपचारिक पैटर्न रहे।]]
१९वीं शदी के बहुत सारा हिस्सा ले यूरोपीय के प्रमुख ताकत सभ आपस में सत्ता के क्षीण संतुलन बनवले रखलीं जेकरा के यूरोप के संगीत कार्यक्रम के नाँव से जानल जाला। {{Sfn|Clark|2013|pp=121–152}} १८४८ के बाद एकरा के कई किसिम के कारण चुनौती दिहल गइल जवना में ब्रिटेन के तथाकथित शानदार अलगाव में वापसी उस्मानी साम्राज्य के पतन आ ओटो वॉन बिस्मार्क के समय में प्रशिया के उदय शामिल रहल। १८६६ के आस्ट्रिया-प्रशियाई लड़ाई से [[जर्मनी]] में प्रशियाई वर्चस्व के स्थापना भइल जबकि १८७०–१८७१ के फ्रांको-प्रशियाई लड़ाई में जीत के चलते बिस्मार्क के जर्मन राज्य सभ के प्रशियाई नेतृत्व में जर्मन साम्राज्य में एकट्ठा करे के इजाजत मिलल। १८७१ के हार के बदला लिहल या फिर प्रतिशोध आ अल्सास-लोरेन के प्रांत सब के बरामद कइल अगिला चालीस साल ले फ्रांस के नीति के प्रमुख विसय बन गइल। {{Sfn|Zeldin|1977|p=117}}
फ्रांस के अलग-थलग करे आ दूगो मोर्चा पर लड़ाई से बचे खातिर बिस्मार्क आस्ट्रिया-हंगरी, [[रूसी साम्राज्य|रूस]] आ जर्मनी के बीच तीन सम्राट के लीग (जर्मन: ''Dreikaiserbund'' ) के बातचीत कइलें। १८७७–१८७८ के रूस-तुर्की लड़ाई में रूस के जीत के बाद, बाल्कन में रूस के परभाव के बारे में ऑस्ट्रिया के चिंता के कारण लीग के भंग क दिहल गइल, ई क्षेत्र ई लोग सब हुत महत्व के सामरिक रुचि के रूप में मानत रहल। एकरे बाद जर्मनी आ ऑस्ट्रिया-हंगरी १८७९ के ड्यूल एलायंस के गठन कइलें, ई ट्रिपल एलायंस बन गइल जब इटली १८८२ में सामिल भइल। {{Sfn|Keegan|1998|p=52}} बिस्मार्क खातिर एह समझौता सभ के मकसद ई रहल कि तीनों साम्राज्य सभ के बीच कौनों बिबाद के समाधान सुनिश्चित कऽ के फ्रांस के अलग कइल जाय जब १८८० में रूस के साथे सीधे बातचीत करे के ब्रिटिश आ फ्रांसीसी कोसिस से एकरा के खतरा पैदा भइल तब ऊ १८८१ में लीग में सुधार कइलें जेकर नवीकरण १८८३ आ १८८५ में भइल। १८८७ में ई समझौता खतम भइला के बाद ऊ एकरे जगह पुनर्बीमा संधि रखलें, ई जर्मनी आ रूस के बीच एगो गुप्त समझौता रहे कि अगर फ्रांस भा आस्ट्रिया-हंगरी में से कवनो एक पर हमला होखे तब तटस्थ रहे के चाहीं। {{Sfn|Medlicott|1945|pp=66–70}}
बिस्मार्क रूस के साथे शांति के जर्मनी के बिदेस नीति के आधार मानत रहले लेकिन १८९० में कैसर बनला के बाद विल्हेम द्वितीय उनका के रिटायर होखे खातिर मजबूर कइले अउरी उनकर नयका चांसलर लियो वॉन कैप्रीवी द्वारा पुनर्बीमा संधि के नवीकरण ना करे खातिर मना लिहल गइल| {{Sfn|Keenan|1986|p=20}} 15 '']'' . _ जबकि ई औपचारिक गठबंधन ना रहलें, [[अफिरका]] आ एशिया में लंबा समय से चलल आ रहल औपनिवेशिक बिबाद सभ के निपटारा कऽ के भविष्य में फ्रांस भा रूस से जुड़ल कौनों भी संघर्ष में ब्रिटिश लोग के प्रवेश के संभावना बन गइल। {{Sfn|Fay|1930|pp=290–293}} {{Sfn|Willmott|2003|p=15}} १९११ में अगादिर संकट के दौरान जर्मनी के खिलाफ फ्रांस के ब्रिटिश आ रूस के समर्थन से इनहन लोग के संबंध मजबूत भइल आ एंग्लो-जर्मन बिछुड़न बढ़ल आ १९१४ में पैदा होखे वाला बिभाजन अउरी गहिराह हो गइल।
=== हथियार के दौड़ बा ===
१८७१ के बाद जर्मन औद्योगिक ताकत में काफी बढ़ती भइल, एकीकृत रीख के निर्माण, फ्रांसीसी क्षतिपूर्ति के भुगतान आ अल्सास-लोरेन के बिलय के कारण। विल्हेम द्वितीय के समर्थन से एडमिरल अल्फ्रेड वॉन तिरपिट्ज आर्थिक शक्ति में एह बढ़ती के परयोग ''कैसरलिचे मरीन'', या इंपीरियल जर्मन नेवी के निर्माण में करे के कोसिस कइलें जे बिस्व नौसैनिक वर्चस्व खातिर ब्रिटिश रॉयल नेवी के साथ मुकाबला कऽ सके। {{Sfn|Willmott|2003|p=21}} इनके सोच पर अमेरिकी नौसेना के रणनीतिकार अल्फ्रेड थायर महान के परभाव पड़ल, इनके तर्क रहल कि ग्लोबल पावर प्रोजेक्शन खातिर नीला पानी के नौसेना के कब्जा बहुत जरूरी बा; तिरपिट्ज अपना किताबन के जर्मन में अनुवाद करावत रहले जबकि विल्हेम अपना सलाहकारन आ वरिष्ठ सैन्य कर्मी लोग खातिर एह किताबन के पढ़े के जरूरत बनवले रहले। {{Sfn|Herwig|1988|pp=72–73}}
हालांकि ई एगो भावनात्मक फैसला भी रहे, जवना के चलते विल्हेम के एक संगे रॉयल नेवी के प्रशंसा अउरी एकरा से आगे निकले के इच्छा रहे। बिस्मार्क के गणना रहे कि जब तक ओकर समुद्री वर्चस्व सुरक्षित रही तब तक ब्रिटेन यूरोप में हस्तक्षेप ना करी लेकिन १८९० में उनुका बर्खास्तगी के चलते नीति में बदलाव भइल अउरी एंग्लो-जर्मन नौसैनिक हथियार के दौड़ शुरू भइल | {{Sfn|Moll|Luebbert|1980|pp=153–185}} तिरपिट्ज द्वारा खर्च कइल गइल भारी रकम के बावजूद, {{HMS|Dreadnought|1906|6}} के लॉन्च{{HMS|Dreadnought|1906|6}} १९०६ में अंग्रेजन के अपना जर्मन प्रतिद्वंद्वी के मुकाबले तकनीकी फायदा देले रहे जवना के ऊ लोग कबो ना हारल| {{Sfn|Willmott|2003|p=21}} अंत में ई दौड़ बिसाल संसाधन सभ के मोड़ के जर्मन नौसेना के निर्माण कइलस जे ब्रिटेन के बिरोध करे खातिर पर्याप्त रहल, बाकी एकरा के ना हरा सके; १९११ में चांसलर थियोबाल्ड वॉन बेथमैन होलवेग हार के कबूल कइलें जेकरा चलते ''रुस्टंगस्वेंडे'' भा 'हथियार के मोड़' बन गइल, जब ऊ नौसेना से खरचा से सेना में बदल दिहलें। {{Sfn|Stevenson|2016|p=45}}
ई फैसला राजनीतिक तनाव में कमी के वजह से ना भइल रहे बल्कि १९०५ के रूस-जापानी युद्ध अउरी ओकरा बाद के क्रांति में रूस के हार से उबरला के लेके जर्मनी के चिंता रहे| फ्रांस के फंडिंग के समर्थन से आर्थिक सुधार के कारण १९०८ के बाद रेलवे आ बुनियादी ढांचा के काफी बिस्तार भइल, खासतौर पर एकरे पच्छिमी सीमावर्ती क्षेत्र सभ में। {{Sfn|Crisp|1976|pp=174–196}} चूँकि जर्मनी आ ऑस्ट्रिया-हंगरी रूस के तुलना में अपना संख्यात्मक हीनता के भरपाई करे खातिर तेजी से जुटान पर निर्भर रहलें, एह अंतर के बंद होखे से पैदा होखे वाला खतरा रॉयल नेवी के मुकाबला करे से ढेर महत्व के रहल। जर्मनी के १९१३ में आपन खड़ा सेना के १७०,००० सैनिकन के बिस्तार कइला के बाद फ्रांस अनिवार्य सैन्य सेवा के दू साल से बढ़ा के तीन साल कऽ दिहलस; अइसने उपाय बाल्कन ताकत आ इटली द्वारा कइल गइल जेकरा चलते उस्मानी आ आस्ट्रिया-हंगरी के खरचा बढ़ल। व्यय के श्रेणीबद्ध करे में अंतर के कारण निरपेक्ष आंकड़ा के गणना कइल मुश्किल बा, काहें से कि एह में अक्सर रेलवे नियर नागरिक बुनियादी ढांचा परियोजना सभ के छोड़ दिहल जाला जेकर सैन्य उपयोग भी रहे। हालाँकि, १९०८ से १९१३ ले छह गो प्रमुख यूरोपीय ताकत सब के रक्षा खरचा में वास्तविक रूप से ५०% से ढेर के बढ़ती भइल। {{Sfn|Stevenson|2016|p=42}}
=== बाल्कन में टकराव के चलते ===
[[File:1908-10-07_-_Moritz_Schiller's_Delicatessen.jpg|alt=Photo of large white building with one signs saying "Moritz Schiller" and another in Arabic; in front is a cluster of people looking at poster on the wall.|right|thumb|1908 में ऑस्ट्रिया के विलय के घोषणा वाला पोस्टर पढ़त साराजेवो के नागरिक]]
१९१४ से पहिले के सालन में बाल्कन में संकट के सिलसिला सुरू भइल काहें से कि अउरी ताकत सभ उस्मानी पतन से फायदा उठावे के कोसिस करत रहलें। जबकि पैन स्लाविक अउरी रूढ़िवादी रूस अपना के सर्बिया अउरी स्लाव राज्यन के रक्षक मानत रहे, ऊ लोग सामरिक रूप से महत्वपूर्ण बोस्पोरस जलडमरूमध्य पऽ बुल्गारिया जईसन महत्वाकांक्षी स्लाव सक्ति के बजाय कमजोर उस्मानी सरकार के नियंत्रण कइल पसन्द करत रहे| चूँकि पूर्वी तुर्की में रूस के आपन महत्वाकांक्षा रहे अउरी बाल्कन में ओह लोग के मुवक्किलन के दावा ओवरलैपिंग रहे एह से ओह लोग के सन्तुलन बनावे से रूसी नीति निर्माता लोग के बंटवारा हो गइल अउरी क्षेत्रीय अस्थिरता अउरी बढ़ गइल| {{Sfn|McMeekin|2015|pp=66–67}}
ऑस्ट्रिया के राजनेता लोग बाल्कन के अपना साम्राज्य के निरंतर अस्तित्व खातिर जरूरी आ सर्बिया के विस्तार के सीधा खतरा मानत रहे| १९०८–१९०९ के बोस्निया संकट तब शुरू भइल जब आस्ट्रिया पहिले के उस्मानी क्षेत्र बोस्निया आ हर्जेगोविना के अपना में मिला लिहलस, जवना पर ऊ १८७८ से कब्जा कइले रहे । उस्मानी साम्राज्य से बल्गेरिया के आजादी के घोषणा के साथ मेल खाए के समय पर बनल एह एकतरफा कार्रवाई के यूरोपीय ताकतन द्वारा निन्दा कइल गइल बाकी एकरा के उलट दिहला पर कवनो सहमति ना रहला के कारण एकरा के स्वीकार कइल गइल। कुछ इतिहासकार लोग एकरा के एगो महत्वपूर्ण बढ़ती के रूप में देखे ला, बाल्कन में आस्ट्रिया के रूस के साथे सहयोग करे के कवनो संभावना खतम हो जाला जबकि सर्बिया आ इटली के साथ सम्बन्ध के नुकसान पहुँचावे ला, दुनों के एह क्षेत्र में आपन विस्तारवादी महत्वाकांक्षा रहे। {{Sfn|Clark|2013|p=86}}
१९११ से १९१२ तक इटालो-तुर्की युद्ध के बाद तनाव बढ़ल जवना से उस्मानी कमजोरी के परदर्शन भइल अउरी एकरा चलते बाल्कन लीग के गठन भइल जवन सर्बिया, बुल्गारिया, मोंटेनेग्रो अउरी ग्रीस के गठबन्धन रहे| {{Sfn|Clark|2013|pp=251–252}} लीग १९१२ से १९१३ ले के पहिला बाल्कन युद्ध में यूरोपीय तुर्की के अधिकतर हिस्सा के जल्दी से ओवररन कऽ दिहलस, बाहरी पर्यवेक्षक लोग के बहुत आश्चर्य भइभइलल। {{Sfn|McMeekin|2015|p=69}} एड्रियाटिक पर बन्दरगाह सभ पर सर्बियाई कब्जा के परिणाम के रूप में २१ नवंबर १९१२ के आंशिक रूप से ऑस्ट्रिया के जुटान भइल जेह में गैलिसिया में रूसी सीमा के किनारे इकाई सभ भी सामिल रहलें। अगिला दिने एगो बइठक में रूस सरकार एकरा जवाब में जुटान ना करे के फैसला कइलसि काहे कि ऊ अइसन युद्ध के जल्दी करे के तइयार ना रहुवे जवना खातिर ऊ लोग अबहीं तइयार ना रहुवे। {{Sfn|McMeekin|2015|p=73}}
महाशक्ति सभ १९१३ के लंदन संधि के माध्यम से नियंत्रण के दोबारा पुष्टि करे के कोसिस कइलस, जेह में एगो स्वतन्त्र [[अल्बानिया|अल्बानिया के]] निर्माण भइल, जबकि बुल्गारिया, सर्बिया, मोंटेनेग्रो आ ग्रीस के इलाका सभ के बिस्तार कइल गइल। हालाँकि, बिजेता लोग के बीच बिबाद के कारण ३३ दिन के दुसरा बाल्कन लड़ाई के सुरुआत भइल, जब बुल्गारिया १६ जून १९१३ के सर्बिया आ ग्रीस पर हमला कइलस; ई हार गइल आ मैसिडोनिया के अधिकतर हिस्सा सर्बिया आ ग्रीस से आ दक्खिनी डोब्रुजा के रोमानिया से हार गइल। {{Sfn|Willmott|2003|pp=2–23}} एकर परिणाम ई भइल कि बाल्कन जुद्ध सभ से फायदा भइल देस सभ, जइसे कि सर्बिया आ ग्रीस, भी अपना "उचित लाभ" से धोखा महसूस कइलें जबकि आस्ट्रिया खातिर ई अइसन उदासीनता के परमान दिहलस जे जर्मनी समेत अउरी ताकत सभ अपना चिन्ता के देखत रहलें। {{Sfn|Clark|2013|p=288}} {{Sfn|Keegan|1998|pp=48–49}}
==संदर्भ==
{{Reflist|33em}}
[[श्रेणी:CS1: Julian–Gregorian uncertainty]]
[[श्रेणी:पहिला बिस्व जुद्ध]]
d2nsbids27srnr5umu5mdlwwpc4o3sz
पवन सिंह
0
65746
803325
800314
2026-07-29T16:21:02Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803325
wikitext
text/x-wiki
{{Infobox person
| name = पवन सिंह
| birth_name = पवन सिंह
| image = Pawan_Singh_in_a_concert.jpg
| alt =
| caption = 2021 में पवन सिंह
| birth_date = {{birth date and age|1986|01|05|df=y}}<ref>{{cite web|url=https://www.india.com/buzz/happy-birthday-pawan-singh-from-locker-me-jawani-to-palangiya-sone-na-diya-bhojpuri-superstars-top-songs-that-took-internet-by-storm-in-2018-watch-3512219/|title=जन्मदिन मुबारक पवन सिंह: लॉकर में जवानी ले पलंगिया सोने न दिया, भोजपुरी सुपरस्टार के सबसे हिट गाना जवन २०१८ में धमाल मचा देले रहे – देखऽ|publisher=india.com
|date=}}</ref>
| birth_place = [[आरा]], [[बिहार]], [[भारत]]
| other_names = {{hlist|पॉवर स्टार|शेर सिंह}}
| education = स्नातक
| alma_mater = [[महाराजा कॉलेज, आरा]]
| occupation = {{hlist|[[भोजपुरी एक्टर लोगन के लिस्ट|एक्टर]]|[[भोजपुरी सिंगर लोगन के लिस्ट|गायक]]}}
| years_active = {{hlist|1997 - वर्तमान (गायक)|2007 - वर्तमान (अभिनेता)}}
| known_for = "लॉलीपॉप लागेलु..." गाना
| height_m = 1.75
| spouse = {{Plainlist|
* {{marriage|नीलम सिंह |2=2014 |3=2015 |end=d.}}
* {{marriage|1=ज्योति सिंह |2=2018}}
}}
| mother = प्रतिमा सिंह
| father = रामा शंकर सिंह
| awards = {{hlist|दादासाहेब फाल्के फिल्म फाउंडेशन अवार्ड (2017) |अंतर्राष्ट्रीय भोजपुरी फिल्म पुरस्कार | 2016 में सर्वश्रेष्ठ पुरुष गायक | २०१७ में सर्वश्रेष्ठ अभिनेता | 2017 में सर्वश्रेष्ठ पुरुष गायक | 2018 में सर्वाधिक लोकप्रिय अभिनेता का पुरस्कार | 2019 में फिल्म "मां तुझे सलाम" के लिए सर्वश्रेष्ठ अभिनेता जूरी पुरस्कार}}
| module ={{Infobox social media personality
| child = yes
| youtube_handle = @PawanSinghOfficial009
| youtube_display_name = Pawan Singh Official
| youtube_years_active = 2009 –अबतक
| youtube_genre = Music
| youtube_subscribers = 6.45 मिलियन<!-- PLEASE DO NOT CHANGE WITHOUT UPDATING stats_update BELOW -->
| youtube_views = 2.131 बिलियन<!-- PLEASE DO NOT CHANGE WITHOUT UPDATING stats_update BELOW -->
| stats_update = 8 जुलाई 2026
}}
| website = {{url|https://pawansingh.co.in/|ऑफिशियल वेबसाइट}}
}}
'''पवन सिंह''' (जनम: 5 जनवरी 1986) भारतीय [[भोजपुरी सिंगर]] अउरी [[भोजपुरी एक्टर|एक्टर]] बाड़न। इ [[आरा]], [[बिहार]], भारत में पैदा भइलें।<ref>{{Cite web|url=http://timesofindia.indiatimes.com/entertainment/bhojpuri/movies/news/Pawan-Singh-and-Munna-Bhai-together-again/articleshow/44752165.cms|title=पवन सिंह अउरी मुन्नाभाई एक संगे फेंन से|date=9 October 2014|website=टाइम्स ऑफ़ इंडिया|access-date=27 October 2014}}</ref> इ अपना एक्टिंग खाती कई अवार्ड भी जीतले बाड़े, जेमे दू गो अंतरास्ट्रीय भोजपुरी फिलिम पुरस्कार अउर दादा साहेब फाल्के फिलिम फाउंडेशन पुरस्कार सामिल बा। इ भोजपुरी फिल्म में सबसे ज्यादा कमाई करे वाला अभिनेता बाड़न। इ अपन एक्टिंग के शुरुआत ''रंगली चुनरिया तोहरे नाम'' (2014) से शुरू कइलें।
== कैरियर ==
सिंह भोजपुरी पॉप एल्बम में मुख्य गायक के रूप में काम कइले बाड़न। इंकर पहिलका एल्बम ''ओढनिया वालीे'' रहे जवन 1997 में रिलीज़ भइल। 2004 में ''रंगली चुनरिया तोहरे नाम'' से फिलिम में अभिनेता के रूप में पहिलका बेरी अइलन। 2008 में सिंह ''लॉलीपॉप लागेलु'' एल्बम निकालले, जवन कुल्ह दुनिया में तहलका मचइलस। 2016 में इहांके अंतरास्ट्रीय भोजपुरी फिल्म अवार्ड्स में सबसे नीमन मरद गायक के पुरस्कार से सम्मानित कइल गइल।<ref name="International Bhojpuri Film Awards">{{Cite web |url=http://www.bhojpurixp.com/ibfa-awards-winners-list-2016/ |title=जितेवालन के लिस्ट|access-date=10 November 2016 |archive-date=14 May 2018 |archive-url=https://web.archive.org/web/20180514092233/http://www.bhojpurixp.com/ibfa-awards-winners-list-2016/ |url-status=live }}</ref>
2020 में , इ आपन हिंदी डेब्यू कइलन ''"कमरिया हिला रही है"'' गाना ले जेकरा में इनका जरे अमरीकी नचनिया अउरी अभिनेत्री लॉरेन गोटिलेब रहली।<ref>{{cite web|url=https://m.hindustantimes.com/bollywood/lollipop-lagelu-singer-pawan-singh-to-make-hindi-debut-under-jackky-bhagnani-s-label/story-CnSHwzh3xduhhelx3ypX2I_amp.html|title=लॉलीपॉप लागेलु के गायक पवन सिंह आपन हिंदी डेब्यू करे जा रहल बाडे जैकी भगनानी के लेबल में|website=Hindustan Times|access-date=February 20, 2020|archive-date=23 June 2020|archive-url=https://web.archive.org/web/20200623043417/https://m.hindustantimes.com/bollywood/lollipop-lagelu-singer-pawan-singh-to-make-hindi-debut-under-jackky-bhagnani-s-label/story-CnSHwzh3xduhhelx3ypX2I_amp.html|url-status=live}}</ref>
== फिलिमोग्राफी ==
{| class="wikitable"
|+फिलिम
| style="background:#ffc;"| {{dagger|alt=Films that have not yet been released}}
| उ फिलिम जे अभिन ले रिलीज नइखे भइल।
|}
{| class="wikitable sortable plainrowheaders"
|- style="text-align:center;"
! scope="col"|साल
! scope="col"|फिलिम
! scope="col"|रोल
! scope="col"|डाइरेक्टर
! scope="col"|भाषा
!टिप्पणियाँ
|-
| 2007
!scope="row"| ''रंगीले चुनरिया तोहरे नाम''
| राजा
|राज कुमार
|भोजपुरी
|
|-
| 2008
!scope="row"| ''भोजपुरिया दरोगा''
| पवन
|सुदीप पांडे
|भोजपुरी
|
|-
| 2008
!scope="row"| ''प्रतिज्ञा (2008 फिलिम)''
| पवन
|सुशील उपाध्याय
|
|
|-
| 2009
!scope="row"| ''सैया के साथ मदैया माई''
|
| आलोक कुमार
|
|
|-
| 2009
!scope="row"| ''जोगीजी धीरे धीरे''
| राजा
|
|
|
|-
| 2009
!scope="row"|''तू जान हौ हमर''
|राज
|
|
|
|-
| 2009
!scope="row"|''उमरिया कैली तोहरे नाम''
|
|
|
|
|-
| 2009
!scope="row"|''प्यार बिना चैन कहा''
|
|
|
|
|-
| 2009
!scope="row"| ''ओढ़निया कमल करे''
| राजा
|आलोक कुमार
|
|
|-
| 2010
!scope="row"| ''दारार''
| किशन
|अनिल सम्राट
|
|
|-
| 2011
!scope="row"|''लड़ाई ला अखिया ये लौंडे राजा''
|
|
|
|
|-
| 2011
!scope="row"|''डोली चड्ढाके दुलहिन ससुराल चली''
|
|
|
|
|-
| 2010
!scope="row"|''देवरा बड़ा सतावेला''
|राज
|राजकुमार आर पांडे
|
|
|-
| 2011
!scope="row"| ''रंगबाज दरोगा''
|
|
|
|
|-
| 2011
!scope="row"| ''कानून हमारा मुठ्ठी में''
|
|
|
|
|-
| 2011
!scope="row"| ''भागजोगनी''
|
|
|
|
|-
| 2011
!scope="row"| ''चोरवा बन दमद''
|
|
|
|
|-
| 2011
!scope="row"|''देवर भाभी''
|राज
|
|
|
|-
| 2011
!scope="row"|''हमारा मति माई दम बा''
|
|
|
|
|-
| 2011
!scope="row"|''गुंडई राज''
|
|
|
|
|-
| 2012
!scope="row"|''सिंदूर दान''
|
|
|
|
|-
| 2012
!scope="row"| ''जब केहु दिल में समा जला''
|
|
|
|
|-
|2012
!scope="row"|''सौगंध गंगा मैया के''
|राज
|
|
|
|-
| 2012
!scope="row"|''रंगबाज राजा''
|राज
|
|
|
|-
| 2012
!scope="row"|''जंग''
|
|
|
|
|-
| 2012
!scope="row"|''भैया के साली ओढ़निया वाली''
| राजा
|आलोक कुमार
|
|
|-
| 2012
!scope="row"| ''एक दूजे के लिए''
| संजू
|
|
|
|-
| 2013
!scope="row"|''बनारस वाली''
|राज
|रमाकांत प्रसाद
|
|
|-
| 2013
!scope="row"|''जिद्दी आशिक''
|राज
|रमाकांत प्रसाद
|
|
|-
| 2013
!scope="row"|''बजरंग''
|बजरंगी
|
|
|
|-
| 2013
!scope="row"| ''ट्रक चालक''
| राजा
|आर राजकुमार पांडेkumar
|
|
|-
|2013
!scope="row"| ''कट्टा तनाल दुपट्टा पर''
| राजा
|राखी सावंती
|
|
|-
| 2013
!scope="row"| ''डकैत''
|
|
|
|
|-
| 2013
!scope="row"|''देश प्रदेश''
|
|
|
|
|-
| 2013
!scope="row"|''खून पशिना''
|
|
|
|
|-
| 2013
!scope="row"|''लावारिश''
|
|
|
|
|-
| 2013
!scope="row"|''वीर बलवान''
|
|
|
|
|-
| 2013
!scope="row"|''अंधी तूफ़ान''
|
|
|
|
|-
| 2013
!scope="row"|''एक और कुरुक्षेत्र''
|
|
|
|
|-
| 2014
!scope="row"|''करेले कमल धरती के लाल''
|
|
|
|
|-
| 2014
!scope="row"|''सैयाजी दिलवा मंगले''
|राज
|राजकुमार आर पांडे
|
|
|-
| 2014
!scope="row"|''योद्धा''
|
|राज सिप्पी
|
|
|-
| 2014
!scope="row"|''प्रतिज्ञा 2''
|पवन
|सुशील कुमार उपाध्याय
|
|
|-
| 2014
!scope="row"|''बाजीगर''
|
|
|
|
|-
| 2014
!scope="row"|''प्यार मोहब्बत जिंदाबाद''
|राज
|विनय बिहारी
|
|
|-
| 2014
!scope="row"|''इंसाफ''
|
|
|
|
|-
| 2014
!scope="row"| ''लेबू का जान''
| किशन
|
|
|
|-
| 2014
!scope="row"|''ठोक देब''
|
|
|
|
|-
| 2015
!scope="row"|''नहले पे दहला''
| राज
|अज्ञात
|
|
|-
| 2015
!scope="row"|''बिन बजाये सपेरा''
|राज
|
|
|
|-
| 2015
!scope="row"|''करज़ विरासत के''
|
|अनिल सिंकरी
|
|
|-
| 2015
!scope="row"|''बैंड बाजा लेके आजा पवन राजा''
|पवन
|दिनेश एस यादव
|
|
|-
| 2015
!scope="row"|''लगी छुटे ना राम''
|
|जगदीश शर्मा
|
|
|-
| 2015
!scope="row"|''संग्राम''
|
|जगदीश शर्मा
|
|
|-
| 2015
!scope="row"|''गंगापुत्र''
|राज
|विनय बिहारी
|
|
|-
| 2015
! scope="row" |''हुकुमत''
| राजा
|प्रेम राय
|
|
|-
| 2015
! scope="row" | ''सुहाग''
| राम
|अजय श्रीवास्तव
|
|
|-
| 2015
! scope="row"|''बाज गेल डंका''
| शेर सिंह
|रवि भूषण
|
|
|-
| 2015
!scope="row"|''मुकाबाला''
|
|
|
|
|-
|2015
!scope="row"|''खून का इल्जाम''
|
|सनोज मिश्रा
|
|
|-
| 2016
!scope="row"|''भोजपुरिया राजा''
|राज
|सुजीत कुमार सिंह
|
|
|-
| 2016
!scope="row"|''गदर''
|राज
|रमाकांत प्रसादी
|
|
|-
| 2016
!scope="row"|''त्रिदेव''
|राज
|अरविंद चौबे
|
|
|-
| 2016
!scope="row"| ''जिद्दी''
|प्रेम
|बालिक
|
|
|-
| 2017
!scope="row"|''सरकार राज (भोजपुरी फिलिम 2017)''
|शनि
|अरविंद चौबे
|
|
|-
| 2017
!scope="row"|''सत्या (भोजपुरी फिलिम 2017)''
|सत्य
|सुजीत कुमार सिंह
|
|
|-
| 2017
!scope="row"| ''तेरे जैसा यार कहां''
|गोपी
|रवि भूषण
|
|
|-
| 2017
!scope="row"|''तबाडाला''
|सूर्यकांति
|विनोद तिवारी
|
|
|-
| 2017
!scope="row"|''धड़कन (2017 फिलिम)''
|शंकरी
|सुजीत कुमार सिंह
|
|
|-
| 2017
!scope="row"|''चुनौती''
|राज
|सतीश जैन
|
|
|-
| 2017
!scope="row"|'' योद्धा अर्जुन पंडित''
|अर्जुन पंडित
|जगदीश ए. शर्मा
|
|
|-
|2018
!scope="row"|''सैया सुपर स्टार''
|पवन
|प्रेम राय
|
|
|-
| 2018
!scope="row"|''वांटेड''
|
|सुजीत कुमार
|
|
|-
| 2018
! scope="row"|''मां तुझे सलाम (2018 फिलिम)''
| बजरंगी
|असलम शेख
|
|
|-
| 2018
!scope="row"|''बलमुआ तोहरे खतीर''
|
|दिनेश यादव
|
|
|-
| 2018
! scope="row"|''लोहा पहलवान''
|
|इकबाल बख्शी
|
|
|-
| 2018
! scope="row" | ''राजा (2018 फिलिम)|राजा''
|राज
|संजय श्रीवास्तव
|
|
|-
| 2019
! scope="row" | ''क्रैक फाइटर''
|शंकर,
सुपर सिंह
| सुजीत कुमार सिंह
|
|
|-
| 2019
! scope="row"|''मैंने उन्को साजन चुन लिया''
|विजय
|देवेंद्र तिवारी
|
|
|-
| 2019
! scope="row"|''जय हिंद (2019 फिलिम)''
|
|फिरोज खान<ref>{{cite news|url=https://m.timesofindia.com/entertainment/bhojpuri/movies/news/jai-hind-pawan-singh-shares-the-new-poster-of-his-upcoming-film/articleshow/70364202.cms | title='जय हिंद': पवन सिंह ने अपनी आने वाली फिलिम का नया पोस्टर शेयर किया जुलाई 2019|archive-date=11 अक्टूबर 2020|archive-url=https://web.archive.org/web/20201011183923/https://timesofindia.indiatimes.com/entertainment/bhojpuri/movies/news/jai-hind|url-status=live}}</ref>
|
|
|-
| 2019
!scope="row"| ''शेर सिंह (2019 फिलिम)''
|
|शशांक राय
|
|
|-
|2021
! scope="row" style="background:#ffc; " |''घातक'' {{dagger|alt=Films that have not yet been released}}
|{{TBA}}
| टीनू वर्मा
|
|
|-
|2021
! scope="row" style="background:#ffc; " |''जहरीला'' {{dagger|alt=Films that have not yet been released}}
|{{TBA}}
|रवि एच कश्यप
|
|
|-
|2021
! scope="row" style="background:#ffc; " | बास (साथ महेश मांजरेकर)
|TBA
|अरविन्द चौबे
|
|
|-
|2021
!हम है राही प्यार के
|TBA
|
|
|
|}
== इहो देखल जाय ==
* [[भोजपुरी सिनेमा]]
* [[भोजपुरी एक्टर लोगन के लिस्ट|भोजपुरी एक्टर]]
* [[भोजपुरी सिंगर लोगन के लिस्ट|भोजपुरी गायक]]
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:जियत लोग]]
[[श्रेणी:1986 में जनम]]
[[श्रेणी:आरा जिला के लोग]]
[[श्रेणी:बिहार के एक्टर]]
[[श्रेणी:भारतीय एक्टर]]
[[श्रेणी:भोजपुरी सिंगर]]
[[श्रेणी:भोजपुरी सिनेमा के मरद एक्टर लोग]]
[[श्रेणी:भोजपुरी प्लेबैक सिंगर]]
{{Actor-stub}}
9gw8shcbnmyd8fsqd4acxkw13yqn98s
Module:Complex date
828
65952
803625
633998
2019-01-24T00:20:51Z
153.185.54.127
Fixed Junk code error
803625
Scribunto
text/plain
--[[
__ __ _ _ ____ _ _ _
| \/ | ___ __| |_ _| | ___ _ / ___|___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_) | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_| |__| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)\____\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This module is intended for creation of complex date phrases in variety of languages.
Once deployed, please do not modify this code without applying the changes first at Module:Complex date/sandbox and testing
at Module:Complex date/sandbox/testcases.
Authors and maintainers:
* User:Sn1per - first draft of the original version
* User:Jarekt - corrections and expansion of the original version
]]
-- List of external modules and functions
local p = {Error = nil}
local i18n = require('Module:i18n/complex date')
local ISOdate = require('Module:ISOdate')._ISOdate
local Calendar = require('Module:Calendar')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
table.insert(langList,math.max(#langList,3),'default')
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
local function formatnum1(numStr, lang)
-- mostly require('Module:Formatnum').formatNum function used to translate a number to use different numeral characters,
-- except that it it does not call that function unless the language is on the list "LList"
local LList = {bn=1,bpy=1,kn=1,hi=1,mr=1,new=1,pa=1,gu=1,fa=1,glk=1,mzn=1,ur=1,ar=1,ckb=1,ks=1,lo=1,['or']=1,bo=1,['ml-old']=1,mn=1,te=1,th=1}
if LList[lang] then -- call only when the language is on the list
numStr = require('Module:Formatnum').formatNum(numStr, lang, 1)
end
return numStr
end
local function getISODate(datestr, datetype, lang, num, case)
-- translate dates in the format YYYY, YYYY-MM, and YYYY-MM-DD
if not case and i18n.Translations[datetype] then
-- look up the grammatical case needed and call ISOdate module
local rec = langSwitch(i18n.Translations[datetype], lang)
if type(rec)=='table' then
case = rec.case[num]
end
end
return ISOdate(datestr, lang, case, '', 1)
end
local function translatePhrase(date1, date2, operation, lang, state)
-- use tables in Module:i18n/complex date to translate a phrase
if not i18n.Translations[operation] then
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: input parameter "%s" is not recognized.</span>', operation or 'nil')
return ''
end
local dateStr = langSwitch(i18n.Translations[operation], lang)
if type(dateStr)=='table' then
dateStr = dateStr[1]
end
if type(dateStr)=='function' then
local success
local nDates = i18n.Translations[operation]['nDates']
if nDates==2 then -- 2 date phrase
success, dateStr = pcall(dateStr, date1, date2, state)
else -- 1 date phrase
success, dateStr = pcall(dateStr, date1, state)
end
end
if type(dateStr)=='string' then
-- replace parts of the string '$date1' and '$date2' with date1 and date2 strings
dateStr = mw.ustring.gsub(dateStr, '$date1', date1)
dateStr = mw.ustring.gsub(dateStr, '$date2', date2)
else
-- Special case of more complex phrases that can be build out of simple phrases
-- If complex case is not translated to "lang" than build it out of simpler ones
local x = dateStr
dateStr = p._complex_date(x.conj, x.adj1, date1, x.units1, x.era1, x.adj2, date2, x.units2, x.era2, lang, 2)
end
return dateStr
end
local function oneDatePhrase(dateStr, adj, era, units, lang, num, case, state)
-- translate a single date phrase
if num==2 then
state.adj, state.era, state.units, state.precision = state.adj2, state.era2, state.units2, state.precision2
end
-- dateStr can have many forms: ISO date, year or a number for
-- decade, century or millennium
if units == '' then -- unit is "year", "month", "day"
dateStr = getISODate(dateStr, adj, lang, num, case)
else -- units is "decade", "century", "millennium''
dateStr = translatePhrase(dateStr, '', units, lang, state)
end
-- add adjective ("early", "mid", etc.) or preposition ("before", "after",
-- "circa", etc.) to the date
if adj ~= '' then
dateStr = translatePhrase(dateStr, '', adj, lang, state)
else -- only era?
dateStr = formatnum1(dateStr, lang)
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function twoDatePhrase(date1, date2, state, lang)
-- translate a double date phrase
local dateStr, case
local era=''
if state.era1 == state.era2 then
-- if both eras are the same than add it only once
era = state.era1
state.era1 = ''
state.era2 = ''
end
case = {nil, nil}
if i18n.Translations[state.conj] then
local rec = langSwitch(i18n.Translations[state.conj], lang)
if type(rec)=='table' then
case = rec.case
end
end
date1 = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, case[1], state)
date2 = oneDatePhrase(date2, state.adj2, state.era2, state.units2, lang, 2, case[2], state)
dateStr = translatePhrase(date1, date2, state.conj, lang, state)
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function otherPhrases(date1, date2, operation, era, lang, state)
-- translate specialized phrases
local dateStr = ''
if operation == 'islamic' then
if date2=='' then date2 = mw.getCurrentFrame():callParserFunction('#time', 'xmY', date1) end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
if era == '' then era = 'ad' end
dateStr = translatePhrase(date1, '', era, lang, state) .. ' (' .. translatePhrase(date2, '', 'ah', lang, state) .. ')'
era = ''
elseif operation == 'julian' then
if not date2 and date1 then -- Convert from Julian to Gregorian calendar date
local JDN = Calendar._date2jdn(date1, 0)
if JDN then
date2 = date1 -- first date is assumed to be Julian
date1 = Calendar._jdn2date(JDN, 1)
end
end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
dateStr = translatePhrase(date1, date2, operation, lang, state)
dateStr = mw.ustring.gsub(mw.ustring.gsub(dateStr, '%( ', '('), ' %)', ')') -- in case date2 is empty
elseif operation == 'turn of the year' or operation == 'turn of the decade' or operation == 'turn of the century' then
if operation == 'turn of the decade' then dt=10 else dt=1 end
if not date2 or date2=='' then date2=tostring(tonumber(date1)-dt) end
if era~='bp' and era~='bc' then date1, date2 = date2, date1 end
if operation == 'turn of the year' then
date1 = ISOdate(date1, lang, '', '', 1)
date2 = ISOdate(date2, lang, '', '', 1)
else
date1 = formatnum1(date1, lang)
date2 = formatnum1(date2, lang)
end
dateStr = translatePhrase(date1, date2, operation, lang, state)
elseif operation == 'year unknown' then
dateStr = translatePhrase('', '', operation, lang, state)
elseif operation == 'unknown' then
dateStr = tostring(mw.message.new( "exif-unknowndate" ):inLanguage( lang ))
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function checkAliases(str1, str2, sType)
-- some inputs have many aliases - reconcile them and ensure string is playing a proper role
local out = ''
if str1 and str1~='' then
a = i18n.Synonyms[str1] -- look up synonyms of "str1"
if a then
out = a[1]
else
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: %s is not recognized.</span>', str1)
end
elseif str2 and str2~='' then -- if "str1" of type "sType" is empty than maybe ...
a = i18n.Synonyms[str2] -- ..."str2" is of the same type and is not empty
if a and a[2]==sType then
out = a[1]
str2 = ''
end
end
return out, str2
end
local function datePrecision(dateStr, units)
-- "in this module "Units" is a string like millennium, century, or decade
-- "precision" is wikibase compatible date precision number: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- based on string or numeric input calculate "Units" and "precision"
local dateNum = tonumber(dateStr);
if type(units)=='number' then
precision = units
if precision>11 then precision=11 end -- clip the range of precision values
if precision==6 then units='millennium'
elseif precision==7 then units='century'
elseif precision==8 then units='decade'
else units = ''
end
elseif type(units)=='string' then
units = string.lower(units);
if units=='millennium' then precision=6
elseif units=='century' then precision=7
elseif units=='decade' then precision=8
else precision=9
end
end
if units=='' or precision==9 then
local sLen = mw.ustring.len(dateStr)
if sLen<= 4 then precision=9
elseif sLen== 7 then precision=10
elseif sLen>=10 then precision=11
end
units=''
end
if precision==6 and dateStr.match( dateStr, '%d000' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/1000) +1)
elseif precision==7 and mw.ustring.match( dateStr, '%d%d00' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/100) +1)
end
return dateStr, units, precision
end
local function isodate2timestamp(dateStr, precision, era)
-- convert date string to timestamps used by Quick Statements
local tStamp = nil
if era == 'ah' or precision<6 then
return nil
elseif era ~= '' then
eraLUT = {ad='+', bc='-', bp='-' }
era = eraLUT[era]
else
era='+'
end
-- convert isodate to timestamp used by quick statements
if precision>=9 then
if string.match(dateStr,"^%d%d%d%d$") then -- if YYYY format
tStamp = era .. dateStr .. '-00-00T00:00:00Z/9'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d$") then -- if YYYY-MM format
tStamp = era .. dateStr .. '-00T00:00:00Z/10'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d%-%d%d$") then -- if YYYY-MM-DD format
tStamp = era .. dateStr .. 'T00:00:00Z/11'
end
elseif precision==8 then -- decade
tStamp = era .. dateStr .. '-00-00T00:00:00Z/8'
elseif precision==7 then -- century
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '50-00-00T00:00:00Z/7'
elseif precision==6 then
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '500-00-00T00:00:00Z/6'
end
return tStamp
end
local function oneDateQScode(dateStr, adj, era, precision)
-- create QuickStatements string for "one date" dates
local outputStr = ''
local d = isodate2timestamp(dateStr, precision, era)
if not d then
return ''
end
local rLUT = { early='Q40719727' , mid='Q40719748', late='Q40719766',
['1quarter']='Q40690303' , ['2quarter']='Q40719649' , ['3quarter']='Q40719662', ['4quarter']='Q40719674',
spring='Q40720559' , summer='Q40720564' , autumn='Q40720568' , winter='Q40720553',
firsthalf='Q40719687', secondhalf='Q40719707' }
local qLUT = {['from']='P580', ['until']='P582', ['after']='P1319', ['before']='P1326'}
local refine = rLUT[adj]
local qualitier = qLUT[adj]
if adj=='' then
outputStr = d
elseif adj=='circa' then
outputStr = d..",P1480,Q5727902"
elseif refine then
outputStr = d..",P4241,"..refine
elseif precision>7 and qualitier then
local century = string.gsub(d, 'Z%/%d+', 'Z/7')
outputStr = century ..",".. qualitier ..","..d
end
return outputStr
end
local function twoDateQScode(date1, date2, state)
-- create QuickStatements string for "two date" dates
if state.adj1~='' or state.adj2~='' or state.era1~=state.era2 then
return ''
end
local outputStr = ''
local d1 = isodate2timestamp(date1, state.precision1, state.era1)
local d2 = isodate2timestamp(date2, state.precision2, state.era2)
if (not d1) or (not d2) then
return ''
end
-- find date with lower precision in common to both dates
local cd
local year1 = string.sub(d1,2,5)
local k = 0
for i = 1,10,1 do
if string.sub(d1,1,i)==string.sub(d2,1,i) then
k = i -- find last matching letter
end
end
if k>=9 then -- same month, since "+YYYY-MM-" is in common
cd = isodate2timestamp(string.sub(d1,2,8), 10, state.era1)
elseif k>=6 and k<9 then -- same year, since "+YYYY-" is in common
cd = isodate2timestamp(year1, 9, state.era1)
elseif k==4 then -- same decade(k=4, precision=8), since "+YYY" is in common
cd = isodate2timestamp(year1, 8, state.era1)
elseif k==3 then -- same century(k=3, precision=7) since "+YY" is in common
local d = tostring(math.floor(tonumber(year1)/100) +1) -- convert 1999 -> 20
cd = isodate2timestamp( d, 7, state.era1)
elseif k==2 then -- same millennium (k=2, precision=6), since "+Y" is in common
local d = tostring(math.floor(tonumber(year1)/1000) +1) -- convert 1999 -> 2
cd = isodate2timestamp( d, 6, state.era1)
else
return ''
end
--if not cd then
-- return ' <br/>error: ' .. d1.." / " .. d2.." / ".. (cd or '') .." / ".. string.sub(d1,2,5).." / " .. string.sub(d2,2,5).." / " .. tostring(k)
--end
--
if state.conj=='from-until' then
outputStr = cd ..",P580,".. d1 ..",P582,".. d2
elseif state.conj=='between' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2
elseif state.conj=='circa2' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2 ..",P1480,Q5727902"
end
return outputStr
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
function p.Era(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local lang = args['lang']
local dateStr = args['date'] or ''
local eraType = string.lower(args['era'] or '')
dateStr = ISOdate(dateStr, lang, '', '', 1)
if eraType then
eraType = checkAliases(eraType ,'','e')
dateStr = translatePhrase(dateStr, '', eraType, lang, {})
end
return dateStr
end
function p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
local Output=''
-- process inputs and save date in state array
local state = {}
state.conj = string.lower(conj or '')
state.adj1 = string.lower(adj1 or '')
state.adj2 = string.lower(adj2 or '')
state.era1 = string.lower(era1 or '')
state.era2 = string.lower(era2 or '')
state.units1 = string.lower(units1 or '')
state.units2 = string.lower(units2 or '')
-- if date 1 is missing but date 2 is provided than swap them
if date1 == '' and date2 ~= '' then
date1 = date2
date2 = ''
state = {adj1 = state.adj2, era1 = state.era2, units1 = state.units2,
adj2 = '', era2 = '', units2 = '', conj=state.conj, num=1}
end
if date2 ~= '' then state.nDates = 2
elseif date1 ~= '' then state.nDates = 1
else state.nDates = 0
end
-- reconcile alternative names for text inputs
local conj = checkAliases(state.conj ,'' ,'j')
state.adj1 ,conj = checkAliases(state.adj1 ,conj,'a')
state.units1,conj = checkAliases(state.units1,conj,'p')
state.era1 ,conj = checkAliases(state.era1 ,conj,'e')
state.special,conj = checkAliases('',conj,'c')
state.adj2 = checkAliases(state.adj2 ,'','a')
state.units2 = checkAliases(state.units2,'','p')
state.era2 = checkAliases(state.era2 ,'','e')
state.conj = conj
state.lang = lang
if p.Error~=nil then
return nil
end
-- calculate date precision value
date1, state.units1, state.precision1 = datePrecision(date1, state.units1)
date2, state.units2, state.precision2 = datePrecision(date2, state.units2)
-- Handle special cases
-- Some complex phrases can be created out of simpler ones. Therefore on pass # 1 we try to create
-- the phrase using complex phrase and if that is not found than on the second pass we try to build
-- the phrase out of the simpler ones
if passNr==1 then
if state.adj1=='circa' and state.nDates == 2 then
state.conj = 'circa2'
state.adj1 = ''
state.adj2 = ''
end
if state.nDates == 2 and state.adj1=='late' and state.adj2=='early' and state.conj=='and'
and state.units1==state.units2 and state.era1==state.era2 then
if state.units1=='century' then
state.conj='turn of the century'
elseif state.units1=='decade' then
state.conj='turn of the decade'
elseif state.units1=='' then
state.conj='turn of the year'
end
state.adj1 = ''
state.adj2 = ''
state.units1 = ''
state.units2 = ''
end
end
local errorStr = string.format(
'\n*conj=%s, adj1=%s, era1=%s, unit1=%s, prec1=%i, adj2=%s, era2=%s, unit2=%s, prec2=%i, special=%s',
state.conj, state.adj1, state.era1, state.units1, state.precision1,
state.adj2, state.era2, state.units2, state.precision2, state.special)
state.adj, state.era, state.units, state.precision = state.adj1, state.era1, state.units1, state.precision1
end
-- call specialized functions
local QScode = ''
if state.special~='' then
Output = otherPhrases(date1, date2, state.special, state.era1, lang, state)
elseif state.conj~='' then
QScode = twoDateQScode(date1, date2, state)
Output = twoDatePhrase(date1, date2, state, lang)
elseif state.adj1~='' or state.era1~='' or state.units1~='' then
Output = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, nil, state)
QScode = oneDateQScode(date1, state.adj1, state.era1, state.precision1)
end
return errorStr
end
-- if there is any wikicode in the string than execute it
if mw.ustring.find(Output, '{') then
Output = mw.getCurrentFrame():preprocess(Output)
end
if QScode and #QScode>0 then
QScode = ' <div style="display: none;">date QS:P,' .. QScode .. '</div>'
end
function p.complex_date(frame)
-- process inputs
local dateStr, Error
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local date1 = args['date1'] or args['2'] or args['date'] or ''
local date2 = args['date2'] or args['3'] or ''
local conj = args['conj'] or args['1'] or ''
local adj1 = args['adj1'] or args['adj'] or ''
local adj2 = args['adj2'] or ''
local units1 = args['precision1'] or args['precision'] or ''
local units2 = args['precision2'] or args['precision'] or ''
local era1 = args['era1'] or args['era'] or ''
local era2 = args['era2'] or args['era'] or ''
local lang = args['lang']
dateStr = p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
if p.Error~=nil then
dateStr = p.Error .. '[[Category:Pages using Complex date template with incorrect parameter]]'
end
return dateStr
end
return p
6mwo4vti5ysgc642szpltekkodpbj56
803626
803625
2019-01-24T00:48:36Z
en>Ferret
0
Undid revision 879882658 by [[Special:Contributions/153.185.54.127|153.185.54.127]] ([[User talk:153.185.54.127|talk]]) Broken every infobox relying on this module.
803626
Scribunto
text/plain
--[[
__ __ _ _ ____ _ _ _
| \/ | ___ __| |_ _| | ___ _ / ___|___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_) | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_| |__| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)\____\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This module is intended for creation of complex date phrases in variety of languages.
Once deployed, please do not modify this code without applying the changes first at Module:Complex date/sandbox and testing
at Module:Complex date/sandbox/testcases.
Authors and maintainers:
* User:Sn1per - first draft of the original version
* User:Jarekt - corrections and expansion of the original version
]]
-- List of external modules and functions
local p = {Error = nil}
local i18n = require('Module:i18n/complex date')
local ISOdate = require('Module:ISOdate')._ISOdate
local Calendar = require('Module:Calendar')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
table.insert(langList,math.max(#langList,2),'default')
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
local function formatnum1(numStr, lang)
-- mostly require('Module:Formatnum').formatNum function used to translate a number to use different numeral characters,
-- except that it it does not call that function unless the language is on the list "LList"
local LList = {bn=1,bpy=1,kn=1,hi=1,mr=1,new=1,pa=1,gu=1,fa=1,glk=1,mzn=1,ur=1,ar=1,ckb=1,ks=1,lo=1,['or']=1,bo=1,['ml-old']=1,mn=1,te=1,th=1}
if LList[lang] then -- call only when the language is on the list
numStr = require('Module:Formatnum').formatNum(numStr, lang, 1)
end
return numStr
end
local function getISODate(datestr, datetype, lang, num, case)
-- translate dates in the format YYYY, YYYY-MM, and YYYY-MM-DD
if not case and i18n.Translations[datetype] then
-- look up the grammatical case needed and call ISOdate module
local rec = langSwitch(i18n.Translations[datetype], lang)
if type(rec)=='table' then
case = rec.case[num]
end
end
return ISOdate(datestr, lang, case, '', 1)
end
local function translatePhrase(date1, date2, operation, lang, state)
-- use tables in Module:i18n/complex date to translate a phrase
if not i18n.Translations[operation] then
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: input parameter "%s" is not recognized.</span>', operation or 'nil')
return ''
end
local dateStr = langSwitch(i18n.Translations[operation], lang)
if type(dateStr)=='table' then
dateStr = dateStr[1]
end
if type(dateStr)=='function' then
local success
local nDates = i18n.Translations[operation]['nDates']
if nDates==2 then -- 2 date phrase
success, dateStr = pcall(dateStr, date1, date2, state)
else -- 1 date phrase
success, dateStr = pcall(dateStr, date1, state)
end
end
if type(dateStr)=='string' then
-- replace parts of the string '$date1' and '$date2' with date1 and date2 strings
dateStr = mw.ustring.gsub(dateStr, '$date1', date1)
dateStr = mw.ustring.gsub(dateStr, '$date2', date2)
else
-- Special case of more complex phrases that can be build out of simple phrases
-- If complex case is not translated to "lang" than build it out of simpler ones
local x = dateStr
dateStr = p._complex_date(x.conj, x.adj1, date1, x.units1, x.era1, x.adj2, date2, x.units2, x.era2, lang, 2)
end
return dateStr
end
local function oneDatePhrase(dateStr, adj, era, units, lang, num, case, state)
-- translate a single date phrase
if num==2 then
state.adj, state.era, state.units, state.precision = state.adj2, state.era2, state.units2, state.precision2
end
-- dateStr can have many forms: ISO date, year or a number for
-- decade, century or millennium
if units == '' then -- unit is "year", "month", "day"
dateStr = getISODate(dateStr, adj, lang, num, case)
else -- units is "decade", "century", "millennium''
dateStr = translatePhrase(dateStr, '', units, lang, state)
end
-- add adjective ("early", "mid", etc.) or preposition ("before", "after",
-- "circa", etc.) to the date
if adj ~= '' then
dateStr = translatePhrase(dateStr, '', adj, lang, state)
else -- only era?
dateStr = formatnum1(dateStr, lang)
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function twoDatePhrase(date1, date2, state, lang)
-- translate a double date phrase
local dateStr, case
local era=''
if state.era1 == state.era2 then
-- if both eras are the same than add it only once
era = state.era1
state.era1 = ''
state.era2 = ''
end
case = {nil, nil}
if i18n.Translations[state.conj] then
local rec = langSwitch(i18n.Translations[state.conj], lang)
if type(rec)=='table' then
case = rec.case
end
end
date1 = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, case[1], state)
date2 = oneDatePhrase(date2, state.adj2, state.era2, state.units2, lang, 2, case[2], state)
dateStr = translatePhrase(date1, date2, state.conj, lang, state)
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function otherPhrases(date1, date2, operation, era, lang, state)
-- translate specialized phrases
local dateStr = ''
if operation == 'islamic' then
if date2=='' then date2 = mw.getCurrentFrame():callParserFunction('#time', 'xmY', date1) end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
if era == '' then era = 'ad' end
dateStr = translatePhrase(date1, '', era, lang, state) .. ' (' .. translatePhrase(date2, '', 'ah', lang, state) .. ')'
era = ''
elseif operation == 'julian' then
if not date2 and date1 then -- Convert from Julian to Gregorian calendar date
local JDN = Calendar._date2jdn(date1, 0)
if JDN then
date2 = date1 -- first date is assumed to be Julian
date1 = Calendar._jdn2date(JDN, 1)
end
end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
dateStr = translatePhrase(date1, date2, operation, lang, state)
dateStr = mw.ustring.gsub(mw.ustring.gsub(dateStr, '%( ', '('), ' %)', ')') -- in case date2 is empty
elseif operation == 'turn of the year' or operation == 'turn of the decade' or operation == 'turn of the century' then
if operation == 'turn of the decade' then dt=10 else dt=1 end
if not date2 or date2=='' then date2=tostring(tonumber(date1)-dt) end
if era~='bp' and era~='bc' then date1, date2 = date2, date1 end
if operation == 'turn of the year' then
date1 = ISOdate(date1, lang, '', '', 1)
date2 = ISOdate(date2, lang, '', '', 1)
else
date1 = formatnum1(date1, lang)
date2 = formatnum1(date2, lang)
end
dateStr = translatePhrase(date1, date2, operation, lang, state)
elseif operation == 'year unknown' then
dateStr = translatePhrase('', '', operation, lang, state)
elseif operation == 'unknown' then
dateStr = tostring(mw.message.new( "exif-unknowndate" ):inLanguage( lang ))
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function checkAliases(str1, str2, sType)
-- some inputs have many aliases - reconcile them and ensure string is playing a proper role
local out = ''
if str1 and str1~='' then
a = i18n.Synonyms[str1] -- look up synonyms of "str1"
if a then
out = a[1]
else
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: %s is not recognized.</span>', str1)
end
elseif str2 and str2~='' then -- if "str1" of type "sType" is empty than maybe ...
a = i18n.Synonyms[str2] -- ..."str2" is of the same type and is not empty
if a and a[2]==sType then
out = a[1]
str2 = ''
end
end
return out, str2
end
local function datePrecision(dateStr, units)
-- "in this module "Units" is a string like millennium, century, or decade
-- "precision" is wikibase compatible date precision number: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- based on string or numeric input calculate "Units" and "precision"
local dateNum = tonumber(dateStr);
if type(units)=='number' then
precision = units
if precision>11 then precision=11 end -- clip the range of precision values
if precision==6 then units='millennium'
elseif precision==7 then units='century'
elseif precision==8 then units='decade'
else units = ''
end
elseif type(units)=='string' then
units = string.lower(units);
if units=='millennium' then precision=6
elseif units=='century' then precision=7
elseif units=='decade' then precision=8
else precision=9
end
end
if units=='' or precision==9 then
local sLen = mw.ustring.len(dateStr)
if sLen<= 4 then precision=9
elseif sLen== 7 then precision=10
elseif sLen>=10 then precision=11
end
units=''
end
if precision==6 and dateStr.match( dateStr, '%d000' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/1000) +1)
elseif precision==7 and mw.ustring.match( dateStr, '%d%d00' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/100) +1)
end
return dateStr, units, precision
end
local function isodate2timestamp(dateStr, precision, era)
-- convert date string to timestamps used by Quick Statements
local tStamp = nil
if era == 'ah' or precision<6 then
return nil
elseif era ~= '' then
eraLUT = {ad='+', bc='-', bp='-' }
era = eraLUT[era]
else
era='+'
end
-- convert isodate to timestamp used by quick statements
if precision>=9 then
if string.match(dateStr,"^%d%d%d%d$") then -- if YYYY format
tStamp = era .. dateStr .. '-00-00T00:00:00Z/9'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d$") then -- if YYYY-MM format
tStamp = era .. dateStr .. '-00T00:00:00Z/10'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d%-%d%d$") then -- if YYYY-MM-DD format
tStamp = era .. dateStr .. 'T00:00:00Z/11'
end
elseif precision==8 then -- decade
tStamp = era .. dateStr .. '-00-00T00:00:00Z/8'
elseif precision==7 then -- century
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '50-00-00T00:00:00Z/7'
elseif precision==6 then
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '500-00-00T00:00:00Z/6'
end
return tStamp
end
local function oneDateQScode(dateStr, adj, era, precision)
-- create QuickStatements string for "one date" dates
local outputStr = ''
local d = isodate2timestamp(dateStr, precision, era)
if not d then
return ''
end
local rLUT = { early='Q40719727' , mid='Q40719748', late='Q40719766',
['1quarter']='Q40690303' , ['2quarter']='Q40719649' , ['3quarter']='Q40719662', ['4quarter']='Q40719674',
spring='Q40720559' , summer='Q40720564' , autumn='Q40720568' , winter='Q40720553',
firsthalf='Q40719687', secondhalf='Q40719707' }
local qLUT = {['from']='P580', ['until']='P582', ['after']='P1319', ['before']='P1326'}
local refine = rLUT[adj]
local qualitier = qLUT[adj]
if adj=='' then
outputStr = d
elseif adj=='circa' then
outputStr = d..",P1480,Q5727902"
elseif refine then
outputStr = d..",P4241,"..refine
elseif precision>7 and qualitier then
local century = string.gsub(d, 'Z%/%d+', 'Z/7')
outputStr = century ..",".. qualitier ..","..d
end
return outputStr
end
local function twoDateQScode(date1, date2, state)
-- create QuickStatements string for "two date" dates
if state.adj1~='' or state.adj2~='' or state.era1~=state.era2 then
return ''
end
local outputStr = ''
local d1 = isodate2timestamp(date1, state.precision1, state.era1)
local d2 = isodate2timestamp(date2, state.precision2, state.era2)
if (not d1) or (not d2) then
return ''
end
-- find date with lower precision in common to both dates
local cd
local year1 = string.sub(d1,2,5)
local k = 0
for i = 1,10,1 do
if string.sub(d1,1,i)==string.sub(d2,1,i) then
k = i -- find last matching letter
end
end
if k>=9 then -- same month, since "+YYYY-MM-" is in common
cd = isodate2timestamp(string.sub(d1,2,8), 10, state.era1)
elseif k>=6 and k<9 then -- same year, since "+YYYY-" is in common
cd = isodate2timestamp(year1, 9, state.era1)
elseif k==4 then -- same decade(k=4, precision=8), since "+YYY" is in common
cd = isodate2timestamp(year1, 8, state.era1)
elseif k==3 then -- same century(k=3, precision=7) since "+YY" is in common
local d = tostring(math.floor(tonumber(year1)/100) +1) -- convert 1999 -> 20
cd = isodate2timestamp( d, 7, state.era1)
elseif k==2 then -- same millennium (k=2, precision=6), since "+Y" is in common
local d = tostring(math.floor(tonumber(year1)/1000) +1) -- convert 1999 -> 2
cd = isodate2timestamp( d, 6, state.era1)
else
return ''
end
--if not cd then
-- return ' <br/>error: ' .. d1.." / " .. d2.." / ".. (cd or '') .." / ".. string.sub(d1,2,5).." / " .. string.sub(d2,2,5).." / " .. tostring(k)
--end
--
if state.conj=='from-until' then
outputStr = cd ..",P580,".. d1 ..",P582,".. d2
elseif state.conj=='between' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2
elseif state.conj=='circa2' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2 ..",P1480,Q5727902"
end
return outputStr
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
function p.Era(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local lang = args['lang']
local dateStr = args['date'] or ''
local eraType = string.lower(args['era'] or '')
dateStr = ISOdate(dateStr, lang, '', '', 1)
if eraType then
eraType = checkAliases(eraType ,'','e')
dateStr = translatePhrase(dateStr, '', eraType, lang, {})
end
return dateStr
end
function p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
local Output=''
-- process inputs and save date in state array
local state = {}
state.conj = string.lower(conj or '')
state.adj1 = string.lower(adj1 or '')
state.adj2 = string.lower(adj2 or '')
state.era1 = string.lower(era1 or '')
state.era2 = string.lower(era2 or '')
state.units1 = string.lower(units1 or '')
state.units2 = string.lower(units2 or '')
-- if date 1 is missing but date 2 is provided than swap them
if date1 == '' and date2 ~= '' then
date1 = date2
date2 = ''
state = {adj1 = state.adj2, era1 = state.era2, units1 = state.units2,
adj2 = '', era2 = '', units2 = '', conj=state.conj, num=1}
end
if date2 ~= '' then state.nDates = 2
elseif date1 ~= '' then state.nDates = 1
else state.nDates = 0
end
-- reconcile alternative names for text inputs
local conj = checkAliases(state.conj ,'' ,'j')
state.adj1 ,conj = checkAliases(state.adj1 ,conj,'a')
state.units1,conj = checkAliases(state.units1,conj,'p')
state.era1 ,conj = checkAliases(state.era1 ,conj,'e')
state.special,conj = checkAliases('',conj,'c')
state.adj2 = checkAliases(state.adj2 ,'','a')
state.units2 = checkAliases(state.units2,'','p')
state.era2 = checkAliases(state.era2 ,'','e')
state.conj = conj
state.lang = lang
if p.Error~=nil then
return nil
end
-- calculate date precision value
date1, state.units1, state.precision1 = datePrecision(date1, state.units1)
date2, state.units2, state.precision2 = datePrecision(date2, state.units2)
-- Handle special cases
-- Some complex phrases can be created out of simpler ones. Therefore on pass # 1 we try to create
-- the phrase using complex phrase and if that is not found than on the second pass we try to build
-- the phrase out of the simpler ones
if passNr==1 then
if state.adj1=='circa' and state.nDates == 2 then
state.conj = 'circa2'
state.adj1 = ''
state.adj2 = ''
end
if state.nDates == 2 and state.adj1=='late' and state.adj2=='early' and state.conj=='and'
and state.units1==state.units2 and state.era1==state.era2 then
if state.units1=='century' then
state.conj='turn of the century'
elseif state.units1=='decade' then
state.conj='turn of the decade'
elseif state.units1=='' then
state.conj='turn of the year'
end
state.adj1 = ''
state.adj2 = ''
state.units1 = ''
state.units2 = ''
end
end
local errorStr = string.format(
'\n*conj=%s, adj1=%s, era1=%s, unit1=%s, prec1=%i, adj2=%s, era2=%s, unit2=%s, prec2=%i, special=%s',
state.conj, state.adj1, state.era1, state.units1, state.precision1,
state.adj2, state.era2, state.units2, state.precision2, state.special)
state.adj, state.era, state.units, state.precision = state.adj1, state.era1, state.units1, state.precision1
-- call specialized functions
local QScode = ''
if state.special~='' then
Output = otherPhrases(date1, date2, state.special, state.era1, lang, state)
elseif state.conj~='' then
QScode = twoDateQScode(date1, date2, state)
Output = twoDatePhrase(date1, date2, state, lang)
elseif state.adj1~='' or state.era1~='' or state.units1~='' then
Output = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, nil, state)
QScode = oneDateQScode(date1, state.adj1, state.era1, state.precision1)
elseif date1~='' then
Output = ISOdate(date1, lang, '', 'dtstart', '100-999')
end
if p.Error~=nil then
return errorStr
end
-- if there is any wikicode in the string than execute it
if mw.ustring.find(Output, '{') then
Output = mw.getCurrentFrame():preprocess(Output)
end
if QScode and #QScode>0 then
QScode = ' <div style="display: none;">date QS:P,' .. QScode .. '</div>'
end
return Output .. QScode
end
function p.complex_date(frame)
-- process inputs
local dateStr, Error
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local date1 = args['date1'] or args['2'] or args['date'] or ''
local date2 = args['date2'] or args['3'] or ''
local conj = args['conj'] or args['1'] or ''
local adj1 = args['adj1'] or args['adj'] or ''
local adj2 = args['adj2'] or ''
local units1 = args['precision1'] or args['precision'] or ''
local units2 = args['precision2'] or args['precision'] or ''
local era1 = args['era1'] or args['era'] or ''
local era2 = args['era2'] or args['era'] or ''
local lang = args['lang']
dateStr = p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
if p.Error~=nil then
dateStr = p.Error .. '[[Category:Pages using Complex date template with incorrect parameter]]'
end
return dateStr
end
return p
jrhelia769tliicwgtvdi07i8r3cz5m
803627
803626
2019-01-24T00:48:49Z
en>Ferret
0
Protected "[[Module:Complex date]]": [[WP:High-risk templates|Highly visible template]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
803626
Scribunto
text/plain
--[[
__ __ _ _ ____ _ _ _
| \/ | ___ __| |_ _| | ___ _ / ___|___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_) | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_| |__| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)\____\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This module is intended for creation of complex date phrases in variety of languages.
Once deployed, please do not modify this code without applying the changes first at Module:Complex date/sandbox and testing
at Module:Complex date/sandbox/testcases.
Authors and maintainers:
* User:Sn1per - first draft of the original version
* User:Jarekt - corrections and expansion of the original version
]]
-- List of external modules and functions
local p = {Error = nil}
local i18n = require('Module:i18n/complex date')
local ISOdate = require('Module:ISOdate')._ISOdate
local Calendar = require('Module:Calendar')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
table.insert(langList,math.max(#langList,2),'default')
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
local function formatnum1(numStr, lang)
-- mostly require('Module:Formatnum').formatNum function used to translate a number to use different numeral characters,
-- except that it it does not call that function unless the language is on the list "LList"
local LList = {bn=1,bpy=1,kn=1,hi=1,mr=1,new=1,pa=1,gu=1,fa=1,glk=1,mzn=1,ur=1,ar=1,ckb=1,ks=1,lo=1,['or']=1,bo=1,['ml-old']=1,mn=1,te=1,th=1}
if LList[lang] then -- call only when the language is on the list
numStr = require('Module:Formatnum').formatNum(numStr, lang, 1)
end
return numStr
end
local function getISODate(datestr, datetype, lang, num, case)
-- translate dates in the format YYYY, YYYY-MM, and YYYY-MM-DD
if not case and i18n.Translations[datetype] then
-- look up the grammatical case needed and call ISOdate module
local rec = langSwitch(i18n.Translations[datetype], lang)
if type(rec)=='table' then
case = rec.case[num]
end
end
return ISOdate(datestr, lang, case, '', 1)
end
local function translatePhrase(date1, date2, operation, lang, state)
-- use tables in Module:i18n/complex date to translate a phrase
if not i18n.Translations[operation] then
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: input parameter "%s" is not recognized.</span>', operation or 'nil')
return ''
end
local dateStr = langSwitch(i18n.Translations[operation], lang)
if type(dateStr)=='table' then
dateStr = dateStr[1]
end
if type(dateStr)=='function' then
local success
local nDates = i18n.Translations[operation]['nDates']
if nDates==2 then -- 2 date phrase
success, dateStr = pcall(dateStr, date1, date2, state)
else -- 1 date phrase
success, dateStr = pcall(dateStr, date1, state)
end
end
if type(dateStr)=='string' then
-- replace parts of the string '$date1' and '$date2' with date1 and date2 strings
dateStr = mw.ustring.gsub(dateStr, '$date1', date1)
dateStr = mw.ustring.gsub(dateStr, '$date2', date2)
else
-- Special case of more complex phrases that can be build out of simple phrases
-- If complex case is not translated to "lang" than build it out of simpler ones
local x = dateStr
dateStr = p._complex_date(x.conj, x.adj1, date1, x.units1, x.era1, x.adj2, date2, x.units2, x.era2, lang, 2)
end
return dateStr
end
local function oneDatePhrase(dateStr, adj, era, units, lang, num, case, state)
-- translate a single date phrase
if num==2 then
state.adj, state.era, state.units, state.precision = state.adj2, state.era2, state.units2, state.precision2
end
-- dateStr can have many forms: ISO date, year or a number for
-- decade, century or millennium
if units == '' then -- unit is "year", "month", "day"
dateStr = getISODate(dateStr, adj, lang, num, case)
else -- units is "decade", "century", "millennium''
dateStr = translatePhrase(dateStr, '', units, lang, state)
end
-- add adjective ("early", "mid", etc.) or preposition ("before", "after",
-- "circa", etc.) to the date
if adj ~= '' then
dateStr = translatePhrase(dateStr, '', adj, lang, state)
else -- only era?
dateStr = formatnum1(dateStr, lang)
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function twoDatePhrase(date1, date2, state, lang)
-- translate a double date phrase
local dateStr, case
local era=''
if state.era1 == state.era2 then
-- if both eras are the same than add it only once
era = state.era1
state.era1 = ''
state.era2 = ''
end
case = {nil, nil}
if i18n.Translations[state.conj] then
local rec = langSwitch(i18n.Translations[state.conj], lang)
if type(rec)=='table' then
case = rec.case
end
end
date1 = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, case[1], state)
date2 = oneDatePhrase(date2, state.adj2, state.era2, state.units2, lang, 2, case[2], state)
dateStr = translatePhrase(date1, date2, state.conj, lang, state)
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function otherPhrases(date1, date2, operation, era, lang, state)
-- translate specialized phrases
local dateStr = ''
if operation == 'islamic' then
if date2=='' then date2 = mw.getCurrentFrame():callParserFunction('#time', 'xmY', date1) end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
if era == '' then era = 'ad' end
dateStr = translatePhrase(date1, '', era, lang, state) .. ' (' .. translatePhrase(date2, '', 'ah', lang, state) .. ')'
era = ''
elseif operation == 'julian' then
if not date2 and date1 then -- Convert from Julian to Gregorian calendar date
local JDN = Calendar._date2jdn(date1, 0)
if JDN then
date2 = date1 -- first date is assumed to be Julian
date1 = Calendar._jdn2date(JDN, 1)
end
end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
dateStr = translatePhrase(date1, date2, operation, lang, state)
dateStr = mw.ustring.gsub(mw.ustring.gsub(dateStr, '%( ', '('), ' %)', ')') -- in case date2 is empty
elseif operation == 'turn of the year' or operation == 'turn of the decade' or operation == 'turn of the century' then
if operation == 'turn of the decade' then dt=10 else dt=1 end
if not date2 or date2=='' then date2=tostring(tonumber(date1)-dt) end
if era~='bp' and era~='bc' then date1, date2 = date2, date1 end
if operation == 'turn of the year' then
date1 = ISOdate(date1, lang, '', '', 1)
date2 = ISOdate(date2, lang, '', '', 1)
else
date1 = formatnum1(date1, lang)
date2 = formatnum1(date2, lang)
end
dateStr = translatePhrase(date1, date2, operation, lang, state)
elseif operation == 'year unknown' then
dateStr = translatePhrase('', '', operation, lang, state)
elseif operation == 'unknown' then
dateStr = tostring(mw.message.new( "exif-unknowndate" ):inLanguage( lang ))
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function checkAliases(str1, str2, sType)
-- some inputs have many aliases - reconcile them and ensure string is playing a proper role
local out = ''
if str1 and str1~='' then
a = i18n.Synonyms[str1] -- look up synonyms of "str1"
if a then
out = a[1]
else
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: %s is not recognized.</span>', str1)
end
elseif str2 and str2~='' then -- if "str1" of type "sType" is empty than maybe ...
a = i18n.Synonyms[str2] -- ..."str2" is of the same type and is not empty
if a and a[2]==sType then
out = a[1]
str2 = ''
end
end
return out, str2
end
local function datePrecision(dateStr, units)
-- "in this module "Units" is a string like millennium, century, or decade
-- "precision" is wikibase compatible date precision number: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- based on string or numeric input calculate "Units" and "precision"
local dateNum = tonumber(dateStr);
if type(units)=='number' then
precision = units
if precision>11 then precision=11 end -- clip the range of precision values
if precision==6 then units='millennium'
elseif precision==7 then units='century'
elseif precision==8 then units='decade'
else units = ''
end
elseif type(units)=='string' then
units = string.lower(units);
if units=='millennium' then precision=6
elseif units=='century' then precision=7
elseif units=='decade' then precision=8
else precision=9
end
end
if units=='' or precision==9 then
local sLen = mw.ustring.len(dateStr)
if sLen<= 4 then precision=9
elseif sLen== 7 then precision=10
elseif sLen>=10 then precision=11
end
units=''
end
if precision==6 and dateStr.match( dateStr, '%d000' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/1000) +1)
elseif precision==7 and mw.ustring.match( dateStr, '%d%d00' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/100) +1)
end
return dateStr, units, precision
end
local function isodate2timestamp(dateStr, precision, era)
-- convert date string to timestamps used by Quick Statements
local tStamp = nil
if era == 'ah' or precision<6 then
return nil
elseif era ~= '' then
eraLUT = {ad='+', bc='-', bp='-' }
era = eraLUT[era]
else
era='+'
end
-- convert isodate to timestamp used by quick statements
if precision>=9 then
if string.match(dateStr,"^%d%d%d%d$") then -- if YYYY format
tStamp = era .. dateStr .. '-00-00T00:00:00Z/9'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d$") then -- if YYYY-MM format
tStamp = era .. dateStr .. '-00T00:00:00Z/10'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d%-%d%d$") then -- if YYYY-MM-DD format
tStamp = era .. dateStr .. 'T00:00:00Z/11'
end
elseif precision==8 then -- decade
tStamp = era .. dateStr .. '-00-00T00:00:00Z/8'
elseif precision==7 then -- century
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '50-00-00T00:00:00Z/7'
elseif precision==6 then
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '500-00-00T00:00:00Z/6'
end
return tStamp
end
local function oneDateQScode(dateStr, adj, era, precision)
-- create QuickStatements string for "one date" dates
local outputStr = ''
local d = isodate2timestamp(dateStr, precision, era)
if not d then
return ''
end
local rLUT = { early='Q40719727' , mid='Q40719748', late='Q40719766',
['1quarter']='Q40690303' , ['2quarter']='Q40719649' , ['3quarter']='Q40719662', ['4quarter']='Q40719674',
spring='Q40720559' , summer='Q40720564' , autumn='Q40720568' , winter='Q40720553',
firsthalf='Q40719687', secondhalf='Q40719707' }
local qLUT = {['from']='P580', ['until']='P582', ['after']='P1319', ['before']='P1326'}
local refine = rLUT[adj]
local qualitier = qLUT[adj]
if adj=='' then
outputStr = d
elseif adj=='circa' then
outputStr = d..",P1480,Q5727902"
elseif refine then
outputStr = d..",P4241,"..refine
elseif precision>7 and qualitier then
local century = string.gsub(d, 'Z%/%d+', 'Z/7')
outputStr = century ..",".. qualitier ..","..d
end
return outputStr
end
local function twoDateQScode(date1, date2, state)
-- create QuickStatements string for "two date" dates
if state.adj1~='' or state.adj2~='' or state.era1~=state.era2 then
return ''
end
local outputStr = ''
local d1 = isodate2timestamp(date1, state.precision1, state.era1)
local d2 = isodate2timestamp(date2, state.precision2, state.era2)
if (not d1) or (not d2) then
return ''
end
-- find date with lower precision in common to both dates
local cd
local year1 = string.sub(d1,2,5)
local k = 0
for i = 1,10,1 do
if string.sub(d1,1,i)==string.sub(d2,1,i) then
k = i -- find last matching letter
end
end
if k>=9 then -- same month, since "+YYYY-MM-" is in common
cd = isodate2timestamp(string.sub(d1,2,8), 10, state.era1)
elseif k>=6 and k<9 then -- same year, since "+YYYY-" is in common
cd = isodate2timestamp(year1, 9, state.era1)
elseif k==4 then -- same decade(k=4, precision=8), since "+YYY" is in common
cd = isodate2timestamp(year1, 8, state.era1)
elseif k==3 then -- same century(k=3, precision=7) since "+YY" is in common
local d = tostring(math.floor(tonumber(year1)/100) +1) -- convert 1999 -> 20
cd = isodate2timestamp( d, 7, state.era1)
elseif k==2 then -- same millennium (k=2, precision=6), since "+Y" is in common
local d = tostring(math.floor(tonumber(year1)/1000) +1) -- convert 1999 -> 2
cd = isodate2timestamp( d, 6, state.era1)
else
return ''
end
--if not cd then
-- return ' <br/>error: ' .. d1.." / " .. d2.." / ".. (cd or '') .." / ".. string.sub(d1,2,5).." / " .. string.sub(d2,2,5).." / " .. tostring(k)
--end
--
if state.conj=='from-until' then
outputStr = cd ..",P580,".. d1 ..",P582,".. d2
elseif state.conj=='between' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2
elseif state.conj=='circa2' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2 ..",P1480,Q5727902"
end
return outputStr
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
function p.Era(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local lang = args['lang']
local dateStr = args['date'] or ''
local eraType = string.lower(args['era'] or '')
dateStr = ISOdate(dateStr, lang, '', '', 1)
if eraType then
eraType = checkAliases(eraType ,'','e')
dateStr = translatePhrase(dateStr, '', eraType, lang, {})
end
return dateStr
end
function p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
local Output=''
-- process inputs and save date in state array
local state = {}
state.conj = string.lower(conj or '')
state.adj1 = string.lower(adj1 or '')
state.adj2 = string.lower(adj2 or '')
state.era1 = string.lower(era1 or '')
state.era2 = string.lower(era2 or '')
state.units1 = string.lower(units1 or '')
state.units2 = string.lower(units2 or '')
-- if date 1 is missing but date 2 is provided than swap them
if date1 == '' and date2 ~= '' then
date1 = date2
date2 = ''
state = {adj1 = state.adj2, era1 = state.era2, units1 = state.units2,
adj2 = '', era2 = '', units2 = '', conj=state.conj, num=1}
end
if date2 ~= '' then state.nDates = 2
elseif date1 ~= '' then state.nDates = 1
else state.nDates = 0
end
-- reconcile alternative names for text inputs
local conj = checkAliases(state.conj ,'' ,'j')
state.adj1 ,conj = checkAliases(state.adj1 ,conj,'a')
state.units1,conj = checkAliases(state.units1,conj,'p')
state.era1 ,conj = checkAliases(state.era1 ,conj,'e')
state.special,conj = checkAliases('',conj,'c')
state.adj2 = checkAliases(state.adj2 ,'','a')
state.units2 = checkAliases(state.units2,'','p')
state.era2 = checkAliases(state.era2 ,'','e')
state.conj = conj
state.lang = lang
if p.Error~=nil then
return nil
end
-- calculate date precision value
date1, state.units1, state.precision1 = datePrecision(date1, state.units1)
date2, state.units2, state.precision2 = datePrecision(date2, state.units2)
-- Handle special cases
-- Some complex phrases can be created out of simpler ones. Therefore on pass # 1 we try to create
-- the phrase using complex phrase and if that is not found than on the second pass we try to build
-- the phrase out of the simpler ones
if passNr==1 then
if state.adj1=='circa' and state.nDates == 2 then
state.conj = 'circa2'
state.adj1 = ''
state.adj2 = ''
end
if state.nDates == 2 and state.adj1=='late' and state.adj2=='early' and state.conj=='and'
and state.units1==state.units2 and state.era1==state.era2 then
if state.units1=='century' then
state.conj='turn of the century'
elseif state.units1=='decade' then
state.conj='turn of the decade'
elseif state.units1=='' then
state.conj='turn of the year'
end
state.adj1 = ''
state.adj2 = ''
state.units1 = ''
state.units2 = ''
end
end
local errorStr = string.format(
'\n*conj=%s, adj1=%s, era1=%s, unit1=%s, prec1=%i, adj2=%s, era2=%s, unit2=%s, prec2=%i, special=%s',
state.conj, state.adj1, state.era1, state.units1, state.precision1,
state.adj2, state.era2, state.units2, state.precision2, state.special)
state.adj, state.era, state.units, state.precision = state.adj1, state.era1, state.units1, state.precision1
-- call specialized functions
local QScode = ''
if state.special~='' then
Output = otherPhrases(date1, date2, state.special, state.era1, lang, state)
elseif state.conj~='' then
QScode = twoDateQScode(date1, date2, state)
Output = twoDatePhrase(date1, date2, state, lang)
elseif state.adj1~='' or state.era1~='' or state.units1~='' then
Output = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, nil, state)
QScode = oneDateQScode(date1, state.adj1, state.era1, state.precision1)
elseif date1~='' then
Output = ISOdate(date1, lang, '', 'dtstart', '100-999')
end
if p.Error~=nil then
return errorStr
end
-- if there is any wikicode in the string than execute it
if mw.ustring.find(Output, '{') then
Output = mw.getCurrentFrame():preprocess(Output)
end
if QScode and #QScode>0 then
QScode = ' <div style="display: none;">date QS:P,' .. QScode .. '</div>'
end
return Output .. QScode
end
function p.complex_date(frame)
-- process inputs
local dateStr, Error
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local date1 = args['date1'] or args['2'] or args['date'] or ''
local date2 = args['date2'] or args['3'] or ''
local conj = args['conj'] or args['1'] or ''
local adj1 = args['adj1'] or args['adj'] or ''
local adj2 = args['adj2'] or ''
local units1 = args['precision1'] or args['precision'] or ''
local units2 = args['precision2'] or args['precision'] or ''
local era1 = args['era1'] or args['era'] or ''
local era2 = args['era2'] or args['era'] or ''
local lang = args['lang']
dateStr = p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
if p.Error~=nil then
dateStr = p.Error .. '[[Category:Pages using Complex date template with incorrect parameter]]'
end
return dateStr
end
return p
jrhelia769tliicwgtvdi07i8r3cz5m
803628
803627
2019-02-21T18:49:00Z
en>Pppery
0
Lazy load [[Module:Calendar]], not actually needed by this module in the way it is used through [[Module:WikidataIB]]
803628
Scribunto
text/plain
--[[
__ __ _ _ ____ _ _ _
| \/ | ___ __| |_ _| | ___ _ / ___|___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_) | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_| |__| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)\____\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This module is intended for creation of complex date phrases in variety of languages.
Once deployed, please do not modify this code without applying the changes first at Module:Complex date/sandbox and testing
at Module:Complex date/sandbox/testcases.
Authors and maintainers:
* User:Sn1per - first draft of the original version
* User:Jarekt - corrections and expansion of the original version
]]
-- List of external modules and functions
local p = {Error = nil}
local i18n = require('Module:i18n/complex date')
local ISOdate = require('Module:ISOdate')._ISOdate
local Calendar -- loaded lazily
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
table.insert(langList,math.max(#langList,2),'default')
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
local function formatnum1(numStr, lang)
-- mostly require('Module:Formatnum').formatNum function used to translate a number to use different numeral characters,
-- except that it it does not call that function unless the language is on the list "LList"
local LList = {bn=1,bpy=1,kn=1,hi=1,mr=1,new=1,pa=1,gu=1,fa=1,glk=1,mzn=1,ur=1,ar=1,ckb=1,ks=1,lo=1,['or']=1,bo=1,['ml-old']=1,mn=1,te=1,th=1}
if LList[lang] then -- call only when the language is on the list
numStr = require('Module:Formatnum').formatNum(numStr, lang, 1)
end
return numStr
end
local function getISODate(datestr, datetype, lang, num, case)
-- translate dates in the format YYYY, YYYY-MM, and YYYY-MM-DD
if not case and i18n.Translations[datetype] then
-- look up the grammatical case needed and call ISOdate module
local rec = langSwitch(i18n.Translations[datetype], lang)
if type(rec)=='table' then
case = rec.case[num]
end
end
return ISOdate(datestr, lang, case, '', 1)
end
local function translatePhrase(date1, date2, operation, lang, state)
-- use tables in Module:i18n/complex date to translate a phrase
if not i18n.Translations[operation] then
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: input parameter "%s" is not recognized.</span>', operation or 'nil')
return ''
end
local dateStr = langSwitch(i18n.Translations[operation], lang)
if type(dateStr)=='table' then
dateStr = dateStr[1]
end
if type(dateStr)=='function' then
local success
local nDates = i18n.Translations[operation]['nDates']
if nDates==2 then -- 2 date phrase
success, dateStr = pcall(dateStr, date1, date2, state)
else -- 1 date phrase
success, dateStr = pcall(dateStr, date1, state)
end
end
if type(dateStr)=='string' then
-- replace parts of the string '$date1' and '$date2' with date1 and date2 strings
dateStr = mw.ustring.gsub(dateStr, '$date1', date1)
dateStr = mw.ustring.gsub(dateStr, '$date2', date2)
else
-- Special case of more complex phrases that can be build out of simple phrases
-- If complex case is not translated to "lang" than build it out of simpler ones
local x = dateStr
dateStr = p._complex_date(x.conj, x.adj1, date1, x.units1, x.era1, x.adj2, date2, x.units2, x.era2, lang, 2)
end
return dateStr
end
local function oneDatePhrase(dateStr, adj, era, units, lang, num, case, state)
-- translate a single date phrase
if num==2 then
state.adj, state.era, state.units, state.precision = state.adj2, state.era2, state.units2, state.precision2
end
-- dateStr can have many forms: ISO date, year or a number for
-- decade, century or millennium
if units == '' then -- unit is "year", "month", "day"
dateStr = getISODate(dateStr, adj, lang, num, case)
else -- units is "decade", "century", "millennium''
dateStr = translatePhrase(dateStr, '', units, lang, state)
end
-- add adjective ("early", "mid", etc.) or preposition ("before", "after",
-- "circa", etc.) to the date
if adj ~= '' then
dateStr = translatePhrase(dateStr, '', adj, lang, state)
else -- only era?
dateStr = formatnum1(dateStr, lang)
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function twoDatePhrase(date1, date2, state, lang)
-- translate a double date phrase
local dateStr, case
local era=''
if state.era1 == state.era2 then
-- if both eras are the same than add it only once
era = state.era1
state.era1 = ''
state.era2 = ''
end
case = {nil, nil}
if i18n.Translations[state.conj] then
local rec = langSwitch(i18n.Translations[state.conj], lang)
if type(rec)=='table' then
case = rec.case
end
end
date1 = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, case[1], state)
date2 = oneDatePhrase(date2, state.adj2, state.era2, state.units2, lang, 2, case[2], state)
dateStr = translatePhrase(date1, date2, state.conj, lang, state)
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function otherPhrases(date1, date2, operation, era, lang, state)
-- translate specialized phrases
local dateStr = ''
if operation == 'islamic' then
if date2=='' then date2 = mw.getCurrentFrame():callParserFunction('#time', 'xmY', date1) end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
if era == '' then era = 'ad' end
dateStr = translatePhrase(date1, '', era, lang, state) .. ' (' .. translatePhrase(date2, '', 'ah', lang, state) .. ')'
era = ''
elseif operation == 'julian' then
if not date2 and date1 then -- Convert from Julian to Gregorian calendar date
if Calendar == nil then
Calendar = require("Module:Calendar")
end
local JDN = Calendar._date2jdn(date1, 0)
if JDN then
date2 = date1 -- first date is assumed to be Julian
date1 = Calendar._jdn2date(JDN, 1)
end
end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
dateStr = translatePhrase(date1, date2, operation, lang, state)
dateStr = mw.ustring.gsub(mw.ustring.gsub(dateStr, '%( ', '('), ' %)', ')') -- in case date2 is empty
elseif operation == 'turn of the year' or operation == 'turn of the decade' or operation == 'turn of the century' then
if operation == 'turn of the decade' then dt=10 else dt=1 end
if not date2 or date2=='' then date2=tostring(tonumber(date1)-dt) end
if era~='bp' and era~='bc' then date1, date2 = date2, date1 end
if operation == 'turn of the year' then
date1 = ISOdate(date1, lang, '', '', 1)
date2 = ISOdate(date2, lang, '', '', 1)
else
date1 = formatnum1(date1, lang)
date2 = formatnum1(date2, lang)
end
dateStr = translatePhrase(date1, date2, operation, lang, state)
elseif operation == 'year unknown' then
dateStr = translatePhrase('', '', operation, lang, state)
elseif operation == 'unknown' then
dateStr = tostring(mw.message.new( "exif-unknowndate" ):inLanguage( lang ))
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function checkAliases(str1, str2, sType)
-- some inputs have many aliases - reconcile them and ensure string is playing a proper role
local out = ''
if str1 and str1~='' then
a = i18n.Synonyms[str1] -- look up synonyms of "str1"
if a then
out = a[1]
else
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: %s is not recognized.</span>', str1)
end
elseif str2 and str2~='' then -- if "str1" of type "sType" is empty than maybe ...
a = i18n.Synonyms[str2] -- ..."str2" is of the same type and is not empty
if a and a[2]==sType then
out = a[1]
str2 = ''
end
end
return out, str2
end
local function datePrecision(dateStr, units)
-- "in this module "Units" is a string like millennium, century, or decade
-- "precision" is wikibase compatible date precision number: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- based on string or numeric input calculate "Units" and "precision"
local dateNum = tonumber(dateStr);
if type(units)=='number' then
precision = units
if precision>11 then precision=11 end -- clip the range of precision values
if precision==6 then units='millennium'
elseif precision==7 then units='century'
elseif precision==8 then units='decade'
else units = ''
end
elseif type(units)=='string' then
units = string.lower(units);
if units=='millennium' then precision=6
elseif units=='century' then precision=7
elseif units=='decade' then precision=8
else precision=9
end
end
if units=='' or precision==9 then
local sLen = mw.ustring.len(dateStr)
if sLen<= 4 then precision=9
elseif sLen== 7 then precision=10
elseif sLen>=10 then precision=11
end
units=''
end
if precision==6 and dateStr.match( dateStr, '%d000' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/1000) +1)
elseif precision==7 and mw.ustring.match( dateStr, '%d%d00' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/100) +1)
end
return dateStr, units, precision
end
local function isodate2timestamp(dateStr, precision, era)
-- convert date string to timestamps used by Quick Statements
local tStamp = nil
if era == 'ah' or precision<6 then
return nil
elseif era ~= '' then
eraLUT = {ad='+', bc='-', bp='-' }
era = eraLUT[era]
else
era='+'
end
-- convert isodate to timestamp used by quick statements
if precision>=9 then
if string.match(dateStr,"^%d%d%d%d$") then -- if YYYY format
tStamp = era .. dateStr .. '-00-00T00:00:00Z/9'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d$") then -- if YYYY-MM format
tStamp = era .. dateStr .. '-00T00:00:00Z/10'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d%-%d%d$") then -- if YYYY-MM-DD format
tStamp = era .. dateStr .. 'T00:00:00Z/11'
end
elseif precision==8 then -- decade
tStamp = era .. dateStr .. '-00-00T00:00:00Z/8'
elseif precision==7 then -- century
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '50-00-00T00:00:00Z/7'
elseif precision==6 then
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '500-00-00T00:00:00Z/6'
end
return tStamp
end
local function oneDateQScode(dateStr, adj, era, precision)
-- create QuickStatements string for "one date" dates
local outputStr = ''
local d = isodate2timestamp(dateStr, precision, era)
if not d then
return ''
end
local rLUT = { early='Q40719727' , mid='Q40719748', late='Q40719766',
['1quarter']='Q40690303' , ['2quarter']='Q40719649' , ['3quarter']='Q40719662', ['4quarter']='Q40719674',
spring='Q40720559' , summer='Q40720564' , autumn='Q40720568' , winter='Q40720553',
firsthalf='Q40719687', secondhalf='Q40719707' }
local qLUT = {['from']='P580', ['until']='P582', ['after']='P1319', ['before']='P1326'}
local refine = rLUT[adj]
local qualitier = qLUT[adj]
if adj=='' then
outputStr = d
elseif adj=='circa' then
outputStr = d..",P1480,Q5727902"
elseif refine then
outputStr = d..",P4241,"..refine
elseif precision>7 and qualitier then
local century = string.gsub(d, 'Z%/%d+', 'Z/7')
outputStr = century ..",".. qualitier ..","..d
end
return outputStr
end
local function twoDateQScode(date1, date2, state)
-- create QuickStatements string for "two date" dates
if state.adj1~='' or state.adj2~='' or state.era1~=state.era2 then
return ''
end
local outputStr = ''
local d1 = isodate2timestamp(date1, state.precision1, state.era1)
local d2 = isodate2timestamp(date2, state.precision2, state.era2)
if (not d1) or (not d2) then
return ''
end
-- find date with lower precision in common to both dates
local cd
local year1 = string.sub(d1,2,5)
local k = 0
for i = 1,10,1 do
if string.sub(d1,1,i)==string.sub(d2,1,i) then
k = i -- find last matching letter
end
end
if k>=9 then -- same month, since "+YYYY-MM-" is in common
cd = isodate2timestamp(string.sub(d1,2,8), 10, state.era1)
elseif k>=6 and k<9 then -- same year, since "+YYYY-" is in common
cd = isodate2timestamp(year1, 9, state.era1)
elseif k==4 then -- same decade(k=4, precision=8), since "+YYY" is in common
cd = isodate2timestamp(year1, 8, state.era1)
elseif k==3 then -- same century(k=3, precision=7) since "+YY" is in common
local d = tostring(math.floor(tonumber(year1)/100) +1) -- convert 1999 -> 20
cd = isodate2timestamp( d, 7, state.era1)
elseif k==2 then -- same millennium (k=2, precision=6), since "+Y" is in common
local d = tostring(math.floor(tonumber(year1)/1000) +1) -- convert 1999 -> 2
cd = isodate2timestamp( d, 6, state.era1)
else
return ''
end
--if not cd then
-- return ' <br/>error: ' .. d1.." / " .. d2.." / ".. (cd or '') .." / ".. string.sub(d1,2,5).." / " .. string.sub(d2,2,5).." / " .. tostring(k)
--end
--
if state.conj=='from-until' then
outputStr = cd ..",P580,".. d1 ..",P582,".. d2
elseif state.conj=='between' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2
elseif state.conj=='circa2' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2 ..",P1480,Q5727902"
end
return outputStr
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
function p.Era(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local lang = args['lang']
local dateStr = args['date'] or ''
local eraType = string.lower(args['era'] or '')
dateStr = ISOdate(dateStr, lang, '', '', 1)
if eraType then
eraType = checkAliases(eraType ,'','e')
dateStr = translatePhrase(dateStr, '', eraType, lang, {})
end
return dateStr
end
function p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
local Output=''
-- process inputs and save date in state array
local state = {}
state.conj = string.lower(conj or '')
state.adj1 = string.lower(adj1 or '')
state.adj2 = string.lower(adj2 or '')
state.era1 = string.lower(era1 or '')
state.era2 = string.lower(era2 or '')
state.units1 = string.lower(units1 or '')
state.units2 = string.lower(units2 or '')
-- if date 1 is missing but date 2 is provided than swap them
if date1 == '' and date2 ~= '' then
date1 = date2
date2 = ''
state = {adj1 = state.adj2, era1 = state.era2, units1 = state.units2,
adj2 = '', era2 = '', units2 = '', conj=state.conj, num=1}
end
if date2 ~= '' then state.nDates = 2
elseif date1 ~= '' then state.nDates = 1
else state.nDates = 0
end
-- reconcile alternative names for text inputs
local conj = checkAliases(state.conj ,'' ,'j')
state.adj1 ,conj = checkAliases(state.adj1 ,conj,'a')
state.units1,conj = checkAliases(state.units1,conj,'p')
state.era1 ,conj = checkAliases(state.era1 ,conj,'e')
state.special,conj = checkAliases('',conj,'c')
state.adj2 = checkAliases(state.adj2 ,'','a')
state.units2 = checkAliases(state.units2,'','p')
state.era2 = checkAliases(state.era2 ,'','e')
state.conj = conj
state.lang = lang
if p.Error~=nil then
return nil
end
-- calculate date precision value
date1, state.units1, state.precision1 = datePrecision(date1, state.units1)
date2, state.units2, state.precision2 = datePrecision(date2, state.units2)
-- Handle special cases
-- Some complex phrases can be created out of simpler ones. Therefore on pass # 1 we try to create
-- the phrase using complex phrase and if that is not found than on the second pass we try to build
-- the phrase out of the simpler ones
if passNr==1 then
if state.adj1=='circa' and state.nDates == 2 then
state.conj = 'circa2'
state.adj1 = ''
state.adj2 = ''
end
if state.nDates == 2 and state.adj1=='late' and state.adj2=='early' and state.conj=='and'
and state.units1==state.units2 and state.era1==state.era2 then
if state.units1=='century' then
state.conj='turn of the century'
elseif state.units1=='decade' then
state.conj='turn of the decade'
elseif state.units1=='' then
state.conj='turn of the year'
end
state.adj1 = ''
state.adj2 = ''
state.units1 = ''
state.units2 = ''
end
end
local errorStr = string.format(
'\n*conj=%s, adj1=%s, era1=%s, unit1=%s, prec1=%i, adj2=%s, era2=%s, unit2=%s, prec2=%i, special=%s',
state.conj, state.adj1, state.era1, state.units1, state.precision1,
state.adj2, state.era2, state.units2, state.precision2, state.special)
state.adj, state.era, state.units, state.precision = state.adj1, state.era1, state.units1, state.precision1
-- call specialized functions
local QScode = ''
if state.special~='' then
Output = otherPhrases(date1, date2, state.special, state.era1, lang, state)
elseif state.conj~='' then
QScode = twoDateQScode(date1, date2, state)
Output = twoDatePhrase(date1, date2, state, lang)
elseif state.adj1~='' or state.era1~='' or state.units1~='' then
Output = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, nil, state)
QScode = oneDateQScode(date1, state.adj1, state.era1, state.precision1)
elseif date1~='' then
Output = ISOdate(date1, lang, '', 'dtstart', '100-999')
end
if p.Error~=nil then
return errorStr
end
-- if there is any wikicode in the string than execute it
if mw.ustring.find(Output, '{') then
Output = mw.getCurrentFrame():preprocess(Output)
end
if QScode and #QScode>0 then
QScode = ' <div style="display: none;">date QS:P,' .. QScode .. '</div>'
end
return Output .. QScode
end
function p.complex_date(frame)
-- process inputs
local dateStr, Error
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local date1 = args['date1'] or args['2'] or args['date'] or ''
local date2 = args['date2'] or args['3'] or ''
local conj = args['conj'] or args['1'] or ''
local adj1 = args['adj1'] or args['adj'] or ''
local adj2 = args['adj2'] or ''
local units1 = args['precision1'] or args['precision'] or ''
local units2 = args['precision2'] or args['precision'] or ''
local era1 = args['era1'] or args['era'] or ''
local era2 = args['era2'] or args['era'] or ''
local lang = args['lang']
dateStr = p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
if p.Error~=nil then
dateStr = p.Error .. '[[Category:Pages using Complex date template with incorrect parameter]]'
end
return dateStr
end
return p
9d1u6w6eaila3f7ct2gwiwxtjrg1eq3
803629
803628
2019-02-26T23:40:28Z
en>RexxS
0
dt should be local {fix from Commons}
803629
Scribunto
text/plain
--[[
__ __ _ _ ____ _ _ _
| \/ | ___ __| |_ _| | ___ _ / ___|___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_) | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_| |__| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)\____\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This module is intended for creation of complex date phrases in variety of languages.
Once deployed, please do not modify this code without applying the changes first at Module:Complex date/sandbox and testing
at Module:Complex date/sandbox/testcases.
Authors and maintainers:
* User:Sn1per - first draft of the original version
* User:Jarekt - corrections and expansion of the original version
]]
-- List of external modules and functions
local p = {Error = nil}
local i18n = require('Module:i18n/complex date')
local ISOdate = require('Module:ISOdate')._ISOdate
local Calendar -- loaded lazily
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
table.insert(langList,math.max(#langList,2),'default')
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
local function formatnum1(numStr, lang)
-- mostly require('Module:Formatnum').formatNum function used to translate a number to use different numeral characters,
-- except that it it does not call that function unless the language is on the list "LList"
local LList = {bn=1,bpy=1,kn=1,hi=1,mr=1,new=1,pa=1,gu=1,fa=1,glk=1,mzn=1,ur=1,ar=1,ckb=1,ks=1,lo=1,['or']=1,bo=1,['ml-old']=1,mn=1,te=1,th=1}
if LList[lang] then -- call only when the language is on the list
numStr = require('Module:Formatnum').formatNum(numStr, lang, 1)
end
return numStr
end
local function getISODate(datestr, datetype, lang, num, case)
-- translate dates in the format YYYY, YYYY-MM, and YYYY-MM-DD
if not case and i18n.Translations[datetype] then
-- look up the grammatical case needed and call ISOdate module
local rec = langSwitch(i18n.Translations[datetype], lang)
if type(rec)=='table' then
case = rec.case[num]
end
end
return ISOdate(datestr, lang, case, '', 1)
end
local function translatePhrase(date1, date2, operation, lang, state)
-- use tables in Module:i18n/complex date to translate a phrase
if not i18n.Translations[operation] then
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: input parameter "%s" is not recognized.</span>', operation or 'nil')
return ''
end
local dateStr = langSwitch(i18n.Translations[operation], lang)
if type(dateStr)=='table' then
dateStr = dateStr[1]
end
if type(dateStr)=='function' then
local success
local nDates = i18n.Translations[operation]['nDates']
if nDates==2 then -- 2 date phrase
success, dateStr = pcall(dateStr, date1, date2, state)
else -- 1 date phrase
success, dateStr = pcall(dateStr, date1, state)
end
end
if type(dateStr)=='string' then
-- replace parts of the string '$date1' and '$date2' with date1 and date2 strings
dateStr = mw.ustring.gsub(dateStr, '$date1', date1)
dateStr = mw.ustring.gsub(dateStr, '$date2', date2)
else
-- Special case of more complex phrases that can be build out of simple phrases
-- If complex case is not translated to "lang" than build it out of simpler ones
local x = dateStr
dateStr = p._complex_date(x.conj, x.adj1, date1, x.units1, x.era1, x.adj2, date2, x.units2, x.era2, lang, 2)
end
return dateStr
end
local function oneDatePhrase(dateStr, adj, era, units, lang, num, case, state)
-- translate a single date phrase
if num==2 then
state.adj, state.era, state.units, state.precision = state.adj2, state.era2, state.units2, state.precision2
end
-- dateStr can have many forms: ISO date, year or a number for
-- decade, century or millennium
if units == '' then -- unit is "year", "month", "day"
dateStr = getISODate(dateStr, adj, lang, num, case)
else -- units is "decade", "century", "millennium''
dateStr = translatePhrase(dateStr, '', units, lang, state)
end
-- add adjective ("early", "mid", etc.) or preposition ("before", "after",
-- "circa", etc.) to the date
if adj ~= '' then
dateStr = translatePhrase(dateStr, '', adj, lang, state)
else -- only era?
dateStr = formatnum1(dateStr, lang)
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function twoDatePhrase(date1, date2, state, lang)
-- translate a double date phrase
local dateStr, case
local era=''
if state.era1 == state.era2 then
-- if both eras are the same than add it only once
era = state.era1
state.era1 = ''
state.era2 = ''
end
case = {nil, nil}
if i18n.Translations[state.conj] then
local rec = langSwitch(i18n.Translations[state.conj], lang)
if type(rec)=='table' then
case = rec.case
end
end
date1 = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, case[1], state)
date2 = oneDatePhrase(date2, state.adj2, state.era2, state.units2, lang, 2, case[2], state)
dateStr = translatePhrase(date1, date2, state.conj, lang, state)
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function otherPhrases(date1, date2, operation, era, lang, state)
-- translate specialized phrases
local dateStr = ''
if operation == 'islamic' then
if date2=='' then date2 = mw.getCurrentFrame():callParserFunction('#time', 'xmY', date1) end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
if era == '' then era = 'ad' end
dateStr = translatePhrase(date1, '', era, lang, state) .. ' (' .. translatePhrase(date2, '', 'ah', lang, state) .. ')'
era = ''
elseif operation == 'julian' then
if not date2 and date1 then -- Convert from Julian to Gregorian calendar date
if Calendar == nil then
Calendar = require("Module:Calendar")
end
local JDN = Calendar._date2jdn(date1, 0)
if JDN then
date2 = date1 -- first date is assumed to be Julian
date1 = Calendar._jdn2date(JDN, 1)
end
end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
dateStr = translatePhrase(date1, date2, operation, lang, state)
dateStr = mw.ustring.gsub(mw.ustring.gsub(dateStr, '%( ', '('), ' %)', ')') -- in case date2 is empty
elseif operation == 'turn of the year' or operation == 'turn of the decade' or operation == 'turn of the century' then
local dt = 1
if operation == 'turn of the decade' then dt=10 end
if not date2 or date2=='' then date2=tostring(tonumber(date1)-dt) end
if era~='bp' and era~='bc' then date1, date2 = date2, date1 end
if operation == 'turn of the year' then
date1 = ISOdate(date1, lang, '', '', 1)
date2 = ISOdate(date2, lang, '', '', 1)
else
date1 = formatnum1(date1, lang)
date2 = formatnum1(date2, lang)
end
dateStr = translatePhrase(date1, date2, operation, lang, state)
elseif operation == 'year unknown' then
dateStr = translatePhrase('', '', operation, lang, state)
elseif operation == 'unknown' then
dateStr = tostring(mw.message.new( "exif-unknowndate" ):inLanguage( lang ))
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function checkAliases(str1, str2, sType)
-- some inputs have many aliases - reconcile them and ensure string is playing a proper role
local out = ''
if str1 and str1~='' then
a = i18n.Synonyms[str1] -- look up synonyms of "str1"
if a then
out = a[1]
else
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: %s is not recognized.</span>', str1)
end
elseif str2 and str2~='' then -- if "str1" of type "sType" is empty than maybe ...
a = i18n.Synonyms[str2] -- ..."str2" is of the same type and is not empty
if a and a[2]==sType then
out = a[1]
str2 = ''
end
end
return out, str2
end
local function datePrecision(dateStr, units)
-- "in this module "Units" is a string like millennium, century, or decade
-- "precision" is wikibase compatible date precision number: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- based on string or numeric input calculate "Units" and "precision"
local dateNum = tonumber(dateStr);
if type(units)=='number' then
precision = units
if precision>11 then precision=11 end -- clip the range of precision values
if precision==6 then units='millennium'
elseif precision==7 then units='century'
elseif precision==8 then units='decade'
else units = ''
end
elseif type(units)=='string' then
units = string.lower(units);
if units=='millennium' then precision=6
elseif units=='century' then precision=7
elseif units=='decade' then precision=8
else precision=9
end
end
if units=='' or precision==9 then
local sLen = mw.ustring.len(dateStr)
if sLen<= 4 then precision=9
elseif sLen== 7 then precision=10
elseif sLen>=10 then precision=11
end
units=''
end
if precision==6 and dateStr.match( dateStr, '%d000' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/1000) +1)
elseif precision==7 and mw.ustring.match( dateStr, '%d%d00' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/100) +1)
end
return dateStr, units, precision
end
local function isodate2timestamp(dateStr, precision, era)
-- convert date string to timestamps used by Quick Statements
local tStamp = nil
if era == 'ah' or precision<6 then
return nil
elseif era ~= '' then
eraLUT = {ad='+', bc='-', bp='-' }
era = eraLUT[era]
else
era='+'
end
-- convert isodate to timestamp used by quick statements
if precision>=9 then
if string.match(dateStr,"^%d%d%d%d$") then -- if YYYY format
tStamp = era .. dateStr .. '-00-00T00:00:00Z/9'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d$") then -- if YYYY-MM format
tStamp = era .. dateStr .. '-00T00:00:00Z/10'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d%-%d%d$") then -- if YYYY-MM-DD format
tStamp = era .. dateStr .. 'T00:00:00Z/11'
end
elseif precision==8 then -- decade
tStamp = era .. dateStr .. '-00-00T00:00:00Z/8'
elseif precision==7 then -- century
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '50-00-00T00:00:00Z/7'
elseif precision==6 then
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '500-00-00T00:00:00Z/6'
end
return tStamp
end
local function oneDateQScode(dateStr, adj, era, precision)
-- create QuickStatements string for "one date" dates
local outputStr = ''
local d = isodate2timestamp(dateStr, precision, era)
if not d then
return ''
end
local rLUT = { early='Q40719727' , mid='Q40719748', late='Q40719766',
['1quarter']='Q40690303' , ['2quarter']='Q40719649' , ['3quarter']='Q40719662', ['4quarter']='Q40719674',
spring='Q40720559' , summer='Q40720564' , autumn='Q40720568' , winter='Q40720553',
firsthalf='Q40719687', secondhalf='Q40719707' }
local qLUT = {['from']='P580', ['until']='P582', ['after']='P1319', ['before']='P1326'}
local refine = rLUT[adj]
local qualitier = qLUT[adj]
if adj=='' then
outputStr = d
elseif adj=='circa' then
outputStr = d..",P1480,Q5727902"
elseif refine then
outputStr = d..",P4241,"..refine
elseif precision>7 and qualitier then
local century = string.gsub(d, 'Z%/%d+', 'Z/7')
outputStr = century ..",".. qualitier ..","..d
end
return outputStr
end
local function twoDateQScode(date1, date2, state)
-- create QuickStatements string for "two date" dates
if state.adj1~='' or state.adj2~='' or state.era1~=state.era2 then
return ''
end
local outputStr = ''
local d1 = isodate2timestamp(date1, state.precision1, state.era1)
local d2 = isodate2timestamp(date2, state.precision2, state.era2)
if (not d1) or (not d2) then
return ''
end
-- find date with lower precision in common to both dates
local cd
local year1 = string.sub(d1,2,5)
local k = 0
for i = 1,10,1 do
if string.sub(d1,1,i)==string.sub(d2,1,i) then
k = i -- find last matching letter
end
end
if k>=9 then -- same month, since "+YYYY-MM-" is in common
cd = isodate2timestamp(string.sub(d1,2,8), 10, state.era1)
elseif k>=6 and k<9 then -- same year, since "+YYYY-" is in common
cd = isodate2timestamp(year1, 9, state.era1)
elseif k==4 then -- same decade(k=4, precision=8), since "+YYY" is in common
cd = isodate2timestamp(year1, 8, state.era1)
elseif k==3 then -- same century(k=3, precision=7) since "+YY" is in common
local d = tostring(math.floor(tonumber(year1)/100) +1) -- convert 1999 -> 20
cd = isodate2timestamp( d, 7, state.era1)
elseif k==2 then -- same millennium (k=2, precision=6), since "+Y" is in common
local d = tostring(math.floor(tonumber(year1)/1000) +1) -- convert 1999 -> 2
cd = isodate2timestamp( d, 6, state.era1)
else
return ''
end
--if not cd then
-- return ' <br/>error: ' .. d1.." / " .. d2.." / ".. (cd or '') .." / ".. string.sub(d1,2,5).." / " .. string.sub(d2,2,5).." / " .. tostring(k)
--end
--
if state.conj=='from-until' then
outputStr = cd ..",P580,".. d1 ..",P582,".. d2
elseif state.conj=='between' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2
elseif state.conj=='circa2' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2 ..",P1480,Q5727902"
end
return outputStr
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
function p.Era(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local lang = args['lang']
local dateStr = args['date'] or ''
local eraType = string.lower(args['era'] or '')
dateStr = ISOdate(dateStr, lang, '', '', 1)
if eraType then
eraType = checkAliases(eraType ,'','e')
dateStr = translatePhrase(dateStr, '', eraType, lang, {})
end
return dateStr
end
function p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
local Output=''
-- process inputs and save date in state array
local state = {}
state.conj = string.lower(conj or '')
state.adj1 = string.lower(adj1 or '')
state.adj2 = string.lower(adj2 or '')
state.era1 = string.lower(era1 or '')
state.era2 = string.lower(era2 or '')
state.units1 = string.lower(units1 or '')
state.units2 = string.lower(units2 or '')
-- if date 1 is missing but date 2 is provided than swap them
if date1 == '' and date2 ~= '' then
date1 = date2
date2 = ''
state = {adj1 = state.adj2, era1 = state.era2, units1 = state.units2,
adj2 = '', era2 = '', units2 = '', conj=state.conj, num=1}
end
if date2 ~= '' then state.nDates = 2
elseif date1 ~= '' then state.nDates = 1
else state.nDates = 0
end
-- reconcile alternative names for text inputs
local conj = checkAliases(state.conj ,'' ,'j')
state.adj1 ,conj = checkAliases(state.adj1 ,conj,'a')
state.units1,conj = checkAliases(state.units1,conj,'p')
state.era1 ,conj = checkAliases(state.era1 ,conj,'e')
state.special,conj = checkAliases('',conj,'c')
state.adj2 = checkAliases(state.adj2 ,'','a')
state.units2 = checkAliases(state.units2,'','p')
state.era2 = checkAliases(state.era2 ,'','e')
state.conj = conj
state.lang = lang
if p.Error~=nil then
return nil
end
-- calculate date precision value
date1, state.units1, state.precision1 = datePrecision(date1, state.units1)
date2, state.units2, state.precision2 = datePrecision(date2, state.units2)
-- Handle special cases
-- Some complex phrases can be created out of simpler ones. Therefore on pass # 1 we try to create
-- the phrase using complex phrase and if that is not found than on the second pass we try to build
-- the phrase out of the simpler ones
if passNr==1 then
if state.adj1=='circa' and state.nDates == 2 then
state.conj = 'circa2'
state.adj1 = ''
state.adj2 = ''
end
if state.nDates == 2 and state.adj1=='late' and state.adj2=='early' and state.conj=='and'
and state.units1==state.units2 and state.era1==state.era2 then
if state.units1=='century' then
state.conj='turn of the century'
elseif state.units1=='decade' then
state.conj='turn of the decade'
elseif state.units1=='' then
state.conj='turn of the year'
end
state.adj1 = ''
state.adj2 = ''
state.units1 = ''
state.units2 = ''
end
end
local errorStr = string.format(
'\n*conj=%s, adj1=%s, era1=%s, unit1=%s, prec1=%i, adj2=%s, era2=%s, unit2=%s, prec2=%i, special=%s',
state.conj, state.adj1, state.era1, state.units1, state.precision1,
state.adj2, state.era2, state.units2, state.precision2, state.special)
state.adj, state.era, state.units, state.precision = state.adj1, state.era1, state.units1, state.precision1
-- call specialized functions
local QScode = ''
if state.special~='' then
Output = otherPhrases(date1, date2, state.special, state.era1, lang, state)
elseif state.conj~='' then
QScode = twoDateQScode(date1, date2, state)
Output = twoDatePhrase(date1, date2, state, lang)
elseif state.adj1~='' or state.era1~='' or state.units1~='' then
Output = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, nil, state)
QScode = oneDateQScode(date1, state.adj1, state.era1, state.precision1)
elseif date1~='' then
Output = ISOdate(date1, lang, '', 'dtstart', '100-999')
end
if p.Error~=nil then
return errorStr
end
-- if there is any wikicode in the string than execute it
if mw.ustring.find(Output, '{') then
Output = mw.getCurrentFrame():preprocess(Output)
end
if QScode and #QScode>0 then
QScode = ' <div style="display: none;">date QS:P,' .. QScode .. '</div>'
end
return Output .. QScode
end
function p.complex_date(frame)
-- process inputs
local dateStr, Error
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local date1 = args['date1'] or args['2'] or args['date'] or ''
local date2 = args['date2'] or args['3'] or ''
local conj = args['conj'] or args['1'] or ''
local adj1 = args['adj1'] or args['adj'] or ''
local adj2 = args['adj2'] or ''
local units1 = args['precision1'] or args['precision'] or ''
local units2 = args['precision2'] or args['precision'] or ''
local era1 = args['era1'] or args['era'] or ''
local era2 = args['era2'] or args['era'] or ''
local lang = args['lang']
dateStr = p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
if p.Error~=nil then
dateStr = p.Error .. '[[Category:Pages using Complex date template with incorrect parameter]]'
end
return dateStr
end
return p
8iu59wdkuz6zpa1pc8c4qaykfl5puov
803630
803629
2020-05-14T21:02:23Z
en>RexxS
0
no globals - change to local
803630
Scribunto
text/plain
--[[
__ __ _ _ ____ _ _ _
| \/ | ___ __| |_ _| | ___ _ / ___|___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_) | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_| |__| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)\____\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This module is intended for creation of complex date phrases in variety of languages.
Once deployed, please do not modify this code without applying the changes first at Module:Complex date/sandbox and testing
at Module:Complex date/sandbox/testcases.
Authors and maintainers:
* User:Sn1per - first draft of the original version
* User:Jarekt - corrections and expansion of the original version
]]
-- List of external modules and functions
local p = {Error = nil}
local i18n = require('Module:i18n/complex date')
local ISOdate = require('Module:ISOdate')._ISOdate
local Calendar -- loaded lazily
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
table.insert(langList,math.max(#langList,2),'default')
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
local function formatnum1(numStr, lang)
-- mostly require('Module:Formatnum').formatNum function used to translate a number to use different numeral characters,
-- except that it it does not call that function unless the language is on the list "LList"
local LList = {bn=1,bpy=1,kn=1,hi=1,mr=1,new=1,pa=1,gu=1,fa=1,glk=1,mzn=1,ur=1,ar=1,ckb=1,ks=1,lo=1,['or']=1,bo=1,['ml-old']=1,mn=1,te=1,th=1}
if LList[lang] then -- call only when the language is on the list
numStr = require('Module:Formatnum').formatNum(numStr, lang, 1)
end
return numStr
end
local function getISODate(datestr, datetype, lang, num, case)
-- translate dates in the format YYYY, YYYY-MM, and YYYY-MM-DD
if not case and i18n.Translations[datetype] then
-- look up the grammatical case needed and call ISOdate module
local rec = langSwitch(i18n.Translations[datetype], lang)
if type(rec)=='table' then
case = rec.case[num]
end
end
return ISOdate(datestr, lang, case, '', 1)
end
local function translatePhrase(date1, date2, operation, lang, state)
-- use tables in Module:i18n/complex date to translate a phrase
if not i18n.Translations[operation] then
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: input parameter "%s" is not recognized.</span>', operation or 'nil')
return ''
end
local dateStr = langSwitch(i18n.Translations[operation], lang)
if type(dateStr)=='table' then
dateStr = dateStr[1]
end
if type(dateStr)=='function' then
local success
local nDates = i18n.Translations[operation]['nDates']
if nDates==2 then -- 2 date phrase
success, dateStr = pcall(dateStr, date1, date2, state)
else -- 1 date phrase
success, dateStr = pcall(dateStr, date1, state)
end
end
if type(dateStr)=='string' then
-- replace parts of the string '$date1' and '$date2' with date1 and date2 strings
dateStr = mw.ustring.gsub(dateStr, '$date1', date1)
dateStr = mw.ustring.gsub(dateStr, '$date2', date2)
else
-- Special case of more complex phrases that can be build out of simple phrases
-- If complex case is not translated to "lang" than build it out of simpler ones
local x = dateStr
dateStr = p._complex_date(x.conj, x.adj1, date1, x.units1, x.era1, x.adj2, date2, x.units2, x.era2, lang, 2)
end
return dateStr
end
local function oneDatePhrase(dateStr, adj, era, units, lang, num, case, state)
-- translate a single date phrase
if num==2 then
state.adj, state.era, state.units, state.precision = state.adj2, state.era2, state.units2, state.precision2
end
-- dateStr can have many forms: ISO date, year or a number for
-- decade, century or millennium
if units == '' then -- unit is "year", "month", "day"
dateStr = getISODate(dateStr, adj, lang, num, case)
else -- units is "decade", "century", "millennium''
dateStr = translatePhrase(dateStr, '', units, lang, state)
end
-- add adjective ("early", "mid", etc.) or preposition ("before", "after",
-- "circa", etc.) to the date
if adj ~= '' then
dateStr = translatePhrase(dateStr, '', adj, lang, state)
else -- only era?
dateStr = formatnum1(dateStr, lang)
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function twoDatePhrase(date1, date2, state, lang)
-- translate a double date phrase
local dateStr, case
local era=''
if state.era1 == state.era2 then
-- if both eras are the same than add it only once
era = state.era1
state.era1 = ''
state.era2 = ''
end
case = {nil, nil}
if i18n.Translations[state.conj] then
local rec = langSwitch(i18n.Translations[state.conj], lang)
if type(rec)=='table' then
case = rec.case
end
end
date1 = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, case[1], state)
date2 = oneDatePhrase(date2, state.adj2, state.era2, state.units2, lang, 2, case[2], state)
dateStr = translatePhrase(date1, date2, state.conj, lang, state)
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function otherPhrases(date1, date2, operation, era, lang, state)
-- translate specialized phrases
local dateStr = ''
if operation == 'islamic' then
if date2=='' then date2 = mw.getCurrentFrame():callParserFunction('#time', 'xmY', date1) end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
if era == '' then era = 'ad' end
dateStr = translatePhrase(date1, '', era, lang, state) .. ' (' .. translatePhrase(date2, '', 'ah', lang, state) .. ')'
era = ''
elseif operation == 'julian' then
if not date2 and date1 then -- Convert from Julian to Gregorian calendar date
if Calendar == nil then
Calendar = require("Module:Calendar")
end
local JDN = Calendar._date2jdn(date1, 0)
if JDN then
date2 = date1 -- first date is assumed to be Julian
date1 = Calendar._jdn2date(JDN, 1)
end
end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
dateStr = translatePhrase(date1, date2, operation, lang, state)
dateStr = mw.ustring.gsub(mw.ustring.gsub(dateStr, '%( ', '('), ' %)', ')') -- in case date2 is empty
elseif operation == 'turn of the year' or operation == 'turn of the decade' or operation == 'turn of the century' then
local dt = 1
if operation == 'turn of the decade' then dt=10 end
if not date2 or date2=='' then date2=tostring(tonumber(date1)-dt) end
if era~='bp' and era~='bc' then date1, date2 = date2, date1 end
if operation == 'turn of the year' then
date1 = ISOdate(date1, lang, '', '', 1)
date2 = ISOdate(date2, lang, '', '', 1)
else
date1 = formatnum1(date1, lang)
date2 = formatnum1(date2, lang)
end
dateStr = translatePhrase(date1, date2, operation, lang, state)
elseif operation == 'year unknown' then
dateStr = translatePhrase('', '', operation, lang, state)
elseif operation == 'unknown' then
dateStr = tostring(mw.message.new( "exif-unknowndate" ):inLanguage( lang ))
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function checkAliases(str1, str2, sType)
-- some inputs have many aliases - reconcile them and ensure string is playing a proper role
local out = ''
if str1 and str1~='' then
local a = i18n.Synonyms[str1] -- look up synonyms of "str1"
if a then
out = a[1]
else
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: %s is not recognized.</span>', str1)
end
elseif str2 and str2~='' then -- if "str1" of type "sType" is empty than maybe ...
a = i18n.Synonyms[str2] -- ..."str2" is of the same type and is not empty
if a and a[2]==sType then
out = a[1]
str2 = ''
end
end
return out, str2
end
local function datePrecision(dateStr, units)
-- "in this module "Units" is a string like millennium, century, or decade
-- "precision" is wikibase compatible date precision number: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- based on string or numeric input calculate "Units" and "precision"
local dateNum = tonumber(dateStr)
local precision
if type(units)=='number' then
precision = units
if precision>11 then precision=11 end -- clip the range of precision values
if precision==6 then units='millennium'
elseif precision==7 then units='century'
elseif precision==8 then units='decade'
else units = ''
end
elseif type(units)=='string' then
units = string.lower(units);
if units=='millennium' then precision=6
elseif units=='century' then precision=7
elseif units=='decade' then precision=8
else precision=9
end
end
if units=='' or precision==9 then
local sLen = mw.ustring.len(dateStr)
if sLen<= 4 then precision=9
elseif sLen== 7 then precision=10
elseif sLen>=10 then precision=11
end
units=''
end
if precision==6 and dateStr.match( dateStr, '%d000' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/1000) +1)
elseif precision==7 and mw.ustring.match( dateStr, '%d%d00' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/100) +1)
end
return dateStr, units, precision
end
local function isodate2timestamp(dateStr, precision, era)
-- convert date string to timestamps used by Quick Statements
local tStamp = nil
if era == 'ah' or precision<6 then
return nil
elseif era ~= '' then
local eraLUT = {ad='+', bc='-', bp='-' }
era = eraLUT[era]
else
era='+'
end
-- convert isodate to timestamp used by quick statements
if precision>=9 then
if string.match(dateStr,"^%d%d%d%d$") then -- if YYYY format
tStamp = era .. dateStr .. '-00-00T00:00:00Z/9'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d$") then -- if YYYY-MM format
tStamp = era .. dateStr .. '-00T00:00:00Z/10'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d%-%d%d$") then -- if YYYY-MM-DD format
tStamp = era .. dateStr .. 'T00:00:00Z/11'
end
elseif precision==8 then -- decade
tStamp = era .. dateStr .. '-00-00T00:00:00Z/8'
elseif precision==7 then -- century
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '50-00-00T00:00:00Z/7'
elseif precision==6 then
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '500-00-00T00:00:00Z/6'
end
return tStamp
end
local function oneDateQScode(dateStr, adj, era, precision)
-- create QuickStatements string for "one date" dates
local outputStr = ''
local d = isodate2timestamp(dateStr, precision, era)
if not d then
return ''
end
local rLUT = { early='Q40719727' , mid='Q40719748', late='Q40719766',
['1quarter']='Q40690303' , ['2quarter']='Q40719649' , ['3quarter']='Q40719662', ['4quarter']='Q40719674',
spring='Q40720559' , summer='Q40720564' , autumn='Q40720568' , winter='Q40720553',
firsthalf='Q40719687', secondhalf='Q40719707' }
local qLUT = {['from']='P580', ['until']='P582', ['after']='P1319', ['before']='P1326'}
local refine = rLUT[adj]
local qualitier = qLUT[adj]
if adj=='' then
outputStr = d
elseif adj=='circa' then
outputStr = d..",P1480,Q5727902"
elseif refine then
outputStr = d..",P4241,"..refine
elseif precision>7 and qualitier then
local century = string.gsub(d, 'Z%/%d+', 'Z/7')
outputStr = century ..",".. qualitier ..","..d
end
return outputStr
end
local function twoDateQScode(date1, date2, state)
-- create QuickStatements string for "two date" dates
if state.adj1~='' or state.adj2~='' or state.era1~=state.era2 then
return ''
end
local outputStr = ''
local d1 = isodate2timestamp(date1, state.precision1, state.era1)
local d2 = isodate2timestamp(date2, state.precision2, state.era2)
if (not d1) or (not d2) then
return ''
end
-- find date with lower precision in common to both dates
local cd
local year1 = string.sub(d1,2,5)
local k = 0
for i = 1,10,1 do
if string.sub(d1,1,i)==string.sub(d2,1,i) then
k = i -- find last matching letter
end
end
if k>=9 then -- same month, since "+YYYY-MM-" is in common
cd = isodate2timestamp(string.sub(d1,2,8), 10, state.era1)
elseif k>=6 and k<9 then -- same year, since "+YYYY-" is in common
cd = isodate2timestamp(year1, 9, state.era1)
elseif k==4 then -- same decade(k=4, precision=8), since "+YYY" is in common
cd = isodate2timestamp(year1, 8, state.era1)
elseif k==3 then -- same century(k=3, precision=7) since "+YY" is in common
local d = tostring(math.floor(tonumber(year1)/100) +1) -- convert 1999 -> 20
cd = isodate2timestamp( d, 7, state.era1)
elseif k==2 then -- same millennium (k=2, precision=6), since "+Y" is in common
local d = tostring(math.floor(tonumber(year1)/1000) +1) -- convert 1999 -> 2
cd = isodate2timestamp( d, 6, state.era1)
else
return ''
end
--if not cd then
-- return ' <br/>error: ' .. d1.." / " .. d2.." / ".. (cd or '') .." / ".. string.sub(d1,2,5).." / " .. string.sub(d2,2,5).." / " .. tostring(k)
--end
--
if state.conj=='from-until' then
outputStr = cd ..",P580,".. d1 ..",P582,".. d2
elseif state.conj=='between' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2
elseif state.conj=='circa2' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2 ..",P1480,Q5727902"
end
return outputStr
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
function p.Era(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local lang = args['lang']
local dateStr = args['date'] or ''
local eraType = string.lower(args['era'] or '')
dateStr = ISOdate(dateStr, lang, '', '', 1)
if eraType then
eraType = checkAliases(eraType ,'','e')
dateStr = translatePhrase(dateStr, '', eraType, lang, {})
end
return dateStr
end
function p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
local Output=''
-- process inputs and save date in state array
local state = {}
state.conj = string.lower(conj or '')
state.adj1 = string.lower(adj1 or '')
state.adj2 = string.lower(adj2 or '')
state.era1 = string.lower(era1 or '')
state.era2 = string.lower(era2 or '')
state.units1 = string.lower(units1 or '')
state.units2 = string.lower(units2 or '')
-- if date 1 is missing but date 2 is provided than swap them
if date1 == '' and date2 ~= '' then
date1 = date2
date2 = ''
state = {adj1 = state.adj2, era1 = state.era2, units1 = state.units2,
adj2 = '', era2 = '', units2 = '', conj=state.conj, num=1}
end
if date2 ~= '' then state.nDates = 2
elseif date1 ~= '' then state.nDates = 1
else state.nDates = 0
end
-- reconcile alternative names for text inputs
local conj = checkAliases(state.conj ,'' ,'j')
state.adj1 ,conj = checkAliases(state.adj1 ,conj,'a')
state.units1,conj = checkAliases(state.units1,conj,'p')
state.era1 ,conj = checkAliases(state.era1 ,conj,'e')
state.special,conj = checkAliases('',conj,'c')
state.adj2 = checkAliases(state.adj2 ,'','a')
state.units2 = checkAliases(state.units2,'','p')
state.era2 = checkAliases(state.era2 ,'','e')
state.conj = conj
state.lang = lang
if p.Error~=nil then
return nil
end
-- calculate date precision value
date1, state.units1, state.precision1 = datePrecision(date1, state.units1)
date2, state.units2, state.precision2 = datePrecision(date2, state.units2)
-- Handle special cases
-- Some complex phrases can be created out of simpler ones. Therefore on pass # 1 we try to create
-- the phrase using complex phrase and if that is not found than on the second pass we try to build
-- the phrase out of the simpler ones
if passNr==1 then
if state.adj1=='circa' and state.nDates == 2 then
state.conj = 'circa2'
state.adj1 = ''
state.adj2 = ''
end
if state.nDates == 2 and state.adj1=='late' and state.adj2=='early' and state.conj=='and'
and state.units1==state.units2 and state.era1==state.era2 then
if state.units1=='century' then
state.conj='turn of the century'
elseif state.units1=='decade' then
state.conj='turn of the decade'
elseif state.units1=='' then
state.conj='turn of the year'
end
state.adj1 = ''
state.adj2 = ''
state.units1 = ''
state.units2 = ''
end
end
local errorStr = string.format(
'\n*conj=%s, adj1=%s, era1=%s, unit1=%s, prec1=%i, adj2=%s, era2=%s, unit2=%s, prec2=%i, special=%s',
state.conj, state.adj1, state.era1, state.units1, state.precision1,
state.adj2, state.era2, state.units2, state.precision2, state.special)
state.adj, state.era, state.units, state.precision = state.adj1, state.era1, state.units1, state.precision1
-- call specialized functions
local QScode = ''
if state.special~='' then
Output = otherPhrases(date1, date2, state.special, state.era1, lang, state)
elseif state.conj~='' then
QScode = twoDateQScode(date1, date2, state)
Output = twoDatePhrase(date1, date2, state, lang)
elseif state.adj1~='' or state.era1~='' or state.units1~='' then
Output = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, nil, state)
QScode = oneDateQScode(date1, state.adj1, state.era1, state.precision1)
elseif date1~='' then
Output = ISOdate(date1, lang, '', 'dtstart', '100-999')
end
if p.Error~=nil then
return errorStr
end
-- if there is any wikicode in the string than execute it
if mw.ustring.find(Output, '{') then
Output = mw.getCurrentFrame():preprocess(Output)
end
if QScode and #QScode>0 then
QScode = ' <div style="display: none;">date QS:P,' .. QScode .. '</div>'
end
return Output .. QScode
end
function p.complex_date(frame)
-- process inputs
local dateStr, Error
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local date1 = args['date1'] or args['2'] or args['date'] or ''
local date2 = args['date2'] or args['3'] or ''
local conj = args['conj'] or args['1'] or ''
local adj1 = args['adj1'] or args['adj'] or ''
local adj2 = args['adj2'] or ''
local units1 = args['precision1'] or args['precision'] or ''
local units2 = args['precision2'] or args['precision'] or ''
local era1 = args['era1'] or args['era'] or ''
local era2 = args['era2'] or args['era'] or ''
local lang = args['lang']
dateStr = p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
if p.Error~=nil then
dateStr = p.Error .. '[[Category:Pages using Complex date template with incorrect parameter]]'
end
return dateStr
end
return p
1kjebocnp7tu6ux43orofpvdbuj54mn
803631
803630
2020-05-15T03:54:48Z
en>Johnuniq
0
clean whitespace
803631
Scribunto
text/plain
--[[
__ __ _ _ ____ _ _ _
| \/ | ___ __| |_ _| | ___ _ / ___|___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_) | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_| |__| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)\____\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This module is intended for creation of complex date phrases in variety of languages.
Once deployed, please do not modify this code without applying the changes first at Module:Complex date/sandbox and testing
at Module:Complex date/sandbox/testcases.
Authors and maintainers:
* User:Sn1per - first draft of the original version
* User:Jarekt - corrections and expansion of the original version
]]
-- List of external modules and functions
local p = {Error = nil}
local i18n = require('Module:i18n/complex date')
local ISOdate = require('Module:ISOdate')._ISOdate
local Calendar -- loaded lazily
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
table.insert(langList,math.max(#langList,2),'default')
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
local function formatnum1(numStr, lang)
-- mostly require('Module:Formatnum').formatNum function used to translate a number to use different numeral characters,
-- except that it it does not call that function unless the language is on the list "LList"
local LList = {bn=1,bpy=1,kn=1,hi=1,mr=1,new=1,pa=1,gu=1,fa=1,glk=1,mzn=1,ur=1,ar=1,ckb=1,ks=1,lo=1,['or']=1,bo=1,['ml-old']=1,mn=1,te=1,th=1}
if LList[lang] then -- call only when the language is on the list
numStr = require('Module:Formatnum').formatNum(numStr, lang, 1)
end
return numStr
end
local function getISODate(datestr, datetype, lang, num, case)
-- translate dates in the format YYYY, YYYY-MM, and YYYY-MM-DD
if not case and i18n.Translations[datetype] then
-- look up the grammatical case needed and call ISOdate module
local rec = langSwitch(i18n.Translations[datetype], lang)
if type(rec)=='table' then
case = rec.case[num]
end
end
return ISOdate(datestr, lang, case, '', 1)
end
local function translatePhrase(date1, date2, operation, lang, state)
-- use tables in Module:i18n/complex date to translate a phrase
if not i18n.Translations[operation] then
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: input parameter "%s" is not recognized.</span>', operation or 'nil')
return ''
end
local dateStr = langSwitch(i18n.Translations[operation], lang)
if type(dateStr)=='table' then
dateStr = dateStr[1]
end
if type(dateStr)=='function' then
local success
local nDates = i18n.Translations[operation]['nDates']
if nDates==2 then -- 2 date phrase
success, dateStr = pcall(dateStr, date1, date2, state)
else -- 1 date phrase
success, dateStr = pcall(dateStr, date1, state)
end
end
if type(dateStr)=='string' then
-- replace parts of the string '$date1' and '$date2' with date1 and date2 strings
dateStr = mw.ustring.gsub(dateStr, '$date1', date1)
dateStr = mw.ustring.gsub(dateStr, '$date2', date2)
else
-- Special case of more complex phrases that can be build out of simple phrases
-- If complex case is not translated to "lang" than build it out of simpler ones
local x = dateStr
dateStr = p._complex_date(x.conj, x.adj1, date1, x.units1, x.era1, x.adj2, date2, x.units2, x.era2, lang, 2)
end
return dateStr
end
local function oneDatePhrase(dateStr, adj, era, units, lang, num, case, state)
-- translate a single date phrase
if num==2 then
state.adj, state.era, state.units, state.precision = state.adj2, state.era2, state.units2, state.precision2
end
-- dateStr can have many forms: ISO date, year or a number for
-- decade, century or millennium
if units == '' then -- unit is "year", "month", "day"
dateStr = getISODate(dateStr, adj, lang, num, case)
else -- units is "decade", "century", "millennium''
dateStr = translatePhrase(dateStr, '', units, lang, state)
end
-- add adjective ("early", "mid", etc.) or preposition ("before", "after",
-- "circa", etc.) to the date
if adj ~= '' then
dateStr = translatePhrase(dateStr, '', adj, lang, state)
else -- only era?
dateStr = formatnum1(dateStr, lang)
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function twoDatePhrase(date1, date2, state, lang)
-- translate a double date phrase
local dateStr, case
local era=''
if state.era1 == state.era2 then
-- if both eras are the same than add it only once
era = state.era1
state.era1 = ''
state.era2 = ''
end
case = {nil, nil}
if i18n.Translations[state.conj] then
local rec = langSwitch(i18n.Translations[state.conj], lang)
if type(rec)=='table' then
case = rec.case
end
end
date1 = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, case[1], state)
date2 = oneDatePhrase(date2, state.adj2, state.era2, state.units2, lang, 2, case[2], state)
dateStr = translatePhrase(date1, date2, state.conj, lang, state)
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function otherPhrases(date1, date2, operation, era, lang, state)
-- translate specialized phrases
local dateStr = ''
if operation == 'islamic' then
if date2=='' then date2 = mw.getCurrentFrame():callParserFunction('#time', 'xmY', date1) end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
if era == '' then era = 'ad' end
dateStr = translatePhrase(date1, '', era, lang, state) .. ' (' .. translatePhrase(date2, '', 'ah', lang, state) .. ')'
era = ''
elseif operation == 'julian' then
if not date2 and date1 then -- Convert from Julian to Gregorian calendar date
if Calendar == nil then
Calendar = require("Module:Calendar")
end
local JDN = Calendar._date2jdn(date1, 0)
if JDN then
date2 = date1 -- first date is assumed to be Julian
date1 = Calendar._jdn2date(JDN, 1)
end
end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
dateStr = translatePhrase(date1, date2, operation, lang, state)
dateStr = mw.ustring.gsub(mw.ustring.gsub(dateStr, '%( ', '('), ' %)', ')') -- in case date2 is empty
elseif operation == 'turn of the year' or operation == 'turn of the decade' or operation == 'turn of the century' then
local dt = 1
if operation == 'turn of the decade' then dt=10 end
if not date2 or date2=='' then date2=tostring(tonumber(date1)-dt) end
if era~='bp' and era~='bc' then date1, date2 = date2, date1 end
if operation == 'turn of the year' then
date1 = ISOdate(date1, lang, '', '', 1)
date2 = ISOdate(date2, lang, '', '', 1)
else
date1 = formatnum1(date1, lang)
date2 = formatnum1(date2, lang)
end
dateStr = translatePhrase(date1, date2, operation, lang, state)
elseif operation == 'year unknown' then
dateStr = translatePhrase('', '', operation, lang, state)
elseif operation == 'unknown' then
dateStr = tostring(mw.message.new( "exif-unknowndate" ):inLanguage( lang ))
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function checkAliases(str1, str2, sType)
-- some inputs have many aliases - reconcile them and ensure string is playing a proper role
local out = ''
if str1 and str1~='' then
local a = i18n.Synonyms[str1] -- look up synonyms of "str1"
if a then
out = a[1]
else
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: %s is not recognized.</span>', str1)
end
elseif str2 and str2~='' then -- if "str1" of type "sType" is empty than maybe ...
a = i18n.Synonyms[str2] -- ..."str2" is of the same type and is not empty
if a and a[2]==sType then
out = a[1]
str2 = ''
end
end
return out, str2
end
local function datePrecision(dateStr, units)
-- "in this module "Units" is a string like millennium, century, or decade
-- "precision" is wikibase compatible date precision number: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- based on string or numeric input calculate "Units" and "precision"
local dateNum = tonumber(dateStr)
local precision
if type(units)=='number' then
precision = units
if precision>11 then precision=11 end -- clip the range of precision values
if precision==6 then units='millennium'
elseif precision==7 then units='century'
elseif precision==8 then units='decade'
else units = ''
end
elseif type(units)=='string' then
units = string.lower(units);
if units=='millennium' then precision=6
elseif units=='century' then precision=7
elseif units=='decade' then precision=8
else precision=9
end
end
if units=='' or precision==9 then
local sLen = mw.ustring.len(dateStr)
if sLen<= 4 then precision=9
elseif sLen== 7 then precision=10
elseif sLen>=10 then precision=11
end
units=''
end
if precision==6 and dateStr.match( dateStr, '%d000' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/1000) +1)
elseif precision==7 and mw.ustring.match( dateStr, '%d%d00' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/100) +1)
end
return dateStr, units, precision
end
local function isodate2timestamp(dateStr, precision, era)
-- convert date string to timestamps used by Quick Statements
local tStamp = nil
if era == 'ah' or precision<6 then
return nil
elseif era ~= '' then
local eraLUT = {ad='+', bc='-', bp='-' }
era = eraLUT[era]
else
era='+'
end
-- convert isodate to timestamp used by quick statements
if precision>=9 then
if string.match(dateStr,"^%d%d%d%d$") then -- if YYYY format
tStamp = era .. dateStr .. '-00-00T00:00:00Z/9'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d$") then -- if YYYY-MM format
tStamp = era .. dateStr .. '-00T00:00:00Z/10'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d%-%d%d$") then -- if YYYY-MM-DD format
tStamp = era .. dateStr .. 'T00:00:00Z/11'
end
elseif precision==8 then -- decade
tStamp = era .. dateStr .. '-00-00T00:00:00Z/8'
elseif precision==7 then -- century
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '50-00-00T00:00:00Z/7'
elseif precision==6 then
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '500-00-00T00:00:00Z/6'
end
return tStamp
end
local function oneDateQScode(dateStr, adj, era, precision)
-- create QuickStatements string for "one date" dates
local outputStr = ''
local d = isodate2timestamp(dateStr, precision, era)
if not d then
return ''
end
local rLUT = { early='Q40719727' , mid='Q40719748', late='Q40719766',
['1quarter']='Q40690303' , ['2quarter']='Q40719649' , ['3quarter']='Q40719662', ['4quarter']='Q40719674',
spring='Q40720559' , summer='Q40720564' , autumn='Q40720568' , winter='Q40720553',
firsthalf='Q40719687', secondhalf='Q40719707' }
local qLUT = {['from']='P580', ['until']='P582', ['after']='P1319', ['before']='P1326'}
local refine = rLUT[adj]
local qualitier = qLUT[adj]
if adj=='' then
outputStr = d
elseif adj=='circa' then
outputStr = d..",P1480,Q5727902"
elseif refine then
outputStr = d..",P4241,"..refine
elseif precision>7 and qualitier then
local century = string.gsub(d, 'Z%/%d+', 'Z/7')
outputStr = century ..",".. qualitier ..","..d
end
return outputStr
end
local function twoDateQScode(date1, date2, state)
-- create QuickStatements string for "two date" dates
if state.adj1~='' or state.adj2~='' or state.era1~=state.era2 then
return ''
end
local outputStr = ''
local d1 = isodate2timestamp(date1, state.precision1, state.era1)
local d2 = isodate2timestamp(date2, state.precision2, state.era2)
if (not d1) or (not d2) then
return ''
end
-- find date with lower precision in common to both dates
local cd
local year1 = string.sub(d1,2,5)
local k = 0
for i = 1,10,1 do
if string.sub(d1,1,i)==string.sub(d2,1,i) then
k = i -- find last matching letter
end
end
if k>=9 then -- same month, since "+YYYY-MM-" is in common
cd = isodate2timestamp(string.sub(d1,2,8), 10, state.era1)
elseif k>=6 and k<9 then -- same year, since "+YYYY-" is in common
cd = isodate2timestamp(year1, 9, state.era1)
elseif k==4 then -- same decade(k=4, precision=8), since "+YYY" is in common
cd = isodate2timestamp(year1, 8, state.era1)
elseif k==3 then -- same century(k=3, precision=7) since "+YY" is in common
local d = tostring(math.floor(tonumber(year1)/100) +1) -- convert 1999 -> 20
cd = isodate2timestamp( d, 7, state.era1)
elseif k==2 then -- same millennium (k=2, precision=6), since "+Y" is in common
local d = tostring(math.floor(tonumber(year1)/1000) +1) -- convert 1999 -> 2
cd = isodate2timestamp( d, 6, state.era1)
else
return ''
end
--if not cd then
-- return ' <br/>error: ' .. d1.." / " .. d2.." / ".. (cd or '') .." / ".. string.sub(d1,2,5).." / " .. string.sub(d2,2,5).." / " .. tostring(k)
--end
--
if state.conj=='from-until' then
outputStr = cd ..",P580,".. d1 ..",P582,".. d2
elseif state.conj=='between' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2
elseif state.conj=='circa2' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2 ..",P1480,Q5727902"
end
return outputStr
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
function p.Era(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local lang = args['lang']
local dateStr = args['date'] or ''
local eraType = string.lower(args['era'] or '')
dateStr = ISOdate(dateStr, lang, '', '', 1)
if eraType then
eraType = checkAliases(eraType ,'','e')
dateStr = translatePhrase(dateStr, '', eraType, lang, {})
end
return dateStr
end
function p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
local Output=''
-- process inputs and save date in state array
local state = {}
state.conj = string.lower(conj or '')
state.adj1 = string.lower(adj1 or '')
state.adj2 = string.lower(adj2 or '')
state.era1 = string.lower(era1 or '')
state.era2 = string.lower(era2 or '')
state.units1 = string.lower(units1 or '')
state.units2 = string.lower(units2 or '')
-- if date 1 is missing but date 2 is provided than swap them
if date1 == '' and date2 ~= '' then
date1 = date2
date2 = ''
state = {adj1 = state.adj2, era1 = state.era2, units1 = state.units2,
adj2 = '', era2 = '', units2 = '', conj=state.conj, num=1}
end
if date2 ~= '' then state.nDates = 2
elseif date1 ~= '' then state.nDates = 1
else state.nDates = 0
end
-- reconcile alternative names for text inputs
local conj = checkAliases(state.conj ,'' ,'j')
state.adj1 ,conj = checkAliases(state.adj1 ,conj,'a')
state.units1,conj = checkAliases(state.units1,conj,'p')
state.era1 ,conj = checkAliases(state.era1 ,conj,'e')
state.special,conj = checkAliases('',conj,'c')
state.adj2 = checkAliases(state.adj2 ,'','a')
state.units2 = checkAliases(state.units2,'','p')
state.era2 = checkAliases(state.era2 ,'','e')
state.conj = conj
state.lang = lang
if p.Error~=nil then
return nil
end
-- calculate date precision value
date1, state.units1, state.precision1 = datePrecision(date1, state.units1)
date2, state.units2, state.precision2 = datePrecision(date2, state.units2)
-- Handle special cases
-- Some complex phrases can be created out of simpler ones. Therefore on pass # 1 we try to create
-- the phrase using complex phrase and if that is not found than on the second pass we try to build
-- the phrase out of the simpler ones
if passNr==1 then
if state.adj1=='circa' and state.nDates == 2 then
state.conj = 'circa2'
state.adj1 = ''
state.adj2 = ''
end
if state.nDates == 2 and state.adj1=='late' and state.adj2=='early' and state.conj=='and'
and state.units1==state.units2 and state.era1==state.era2 then
if state.units1=='century' then
state.conj='turn of the century'
elseif state.units1=='decade' then
state.conj='turn of the decade'
elseif state.units1=='' then
state.conj='turn of the year'
end
state.adj1 = ''
state.adj2 = ''
state.units1 = ''
state.units2 = ''
end
end
local errorStr = string.format(
'\n*conj=%s, adj1=%s, era1=%s, unit1=%s, prec1=%i, adj2=%s, era2=%s, unit2=%s, prec2=%i, special=%s',
state.conj, state.adj1, state.era1, state.units1, state.precision1,
state.adj2, state.era2, state.units2, state.precision2, state.special)
state.adj, state.era, state.units, state.precision = state.adj1, state.era1, state.units1, state.precision1
-- call specialized functions
local QScode = ''
if state.special~='' then
Output = otherPhrases(date1, date2, state.special, state.era1, lang, state)
elseif state.conj~='' then
QScode = twoDateQScode(date1, date2, state)
Output = twoDatePhrase(date1, date2, state, lang)
elseif state.adj1~='' or state.era1~='' or state.units1~='' then
Output = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, nil, state)
QScode = oneDateQScode(date1, state.adj1, state.era1, state.precision1)
elseif date1~='' then
Output = ISOdate(date1, lang, '', 'dtstart', '100-999')
end
if p.Error~=nil then
return errorStr
end
-- if there is any wikicode in the string than execute it
if mw.ustring.find(Output, '{') then
Output = mw.getCurrentFrame():preprocess(Output)
end
if QScode and #QScode>0 then
QScode = ' <div style="display: none;">date QS:P,' .. QScode .. '</div>'
end
return Output .. QScode
end
function p.complex_date(frame)
-- process inputs
local dateStr, Error
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local date1 = args['date1'] or args['2'] or args['date'] or ''
local date2 = args['date2'] or args['3'] or ''
local conj = args['conj'] or args['1'] or ''
local adj1 = args['adj1'] or args['adj'] or ''
local adj2 = args['adj2'] or ''
local units1 = args['precision1'] or args['precision'] or ''
local units2 = args['precision2'] or args['precision'] or ''
local era1 = args['era1'] or args['era'] or ''
local era2 = args['era2'] or args['era'] or ''
local lang = args['lang']
dateStr = p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
if p.Error~=nil then
dateStr = p.Error .. '[[Category:Pages using Complex date template with incorrect parameter]]'
end
return dateStr
end
return p
ljyoask03usxmzzkva29k5qja8ngyvd
803632
803631
2020-05-15T03:56:09Z
en>Johnuniq
0
fix one more global problem; couple of minor cleanups; this fixes errors in a few articles such as [[Alexey Verstovsky]]
803632
Scribunto
text/plain
--[[
__ __ _ _ ____ _ _ _
| \/ | ___ __| |_ _| | ___ _ / ___|___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_) | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_| |__| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)\____\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This module is intended for creation of complex date phrases in variety of languages.
Once deployed, please do not modify this code without applying the changes first at Module:Complex date/sandbox and testing
at Module:Complex date/sandbox/testcases.
Authors and maintainers:
* User:Sn1per - first draft of the original version
* User:Jarekt - corrections and expansion of the original version
]]
-- List of external modules and functions
local p = {Error = nil}
local i18n = require('Module:i18n/complex date')
local ISOdate = require('Module:ISOdate')._ISOdate
local Calendar -- loaded lazily
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
table.insert(langList,math.max(#langList,2),'default')
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
local function formatnum1(numStr, lang)
-- mostly require('Module:Formatnum').formatNum function used to translate a number to use different numeral characters,
-- except that it it does not call that function unless the language is on the list "LList"
local LList = {bn=1,bpy=1,kn=1,hi=1,mr=1,new=1,pa=1,gu=1,fa=1,glk=1,mzn=1,ur=1,ar=1,ckb=1,ks=1,lo=1,['or']=1,bo=1,['ml-old']=1,mn=1,te=1,th=1}
if LList[lang] then -- call only when the language is on the list
numStr = require('Module:Formatnum').formatNum(numStr, lang, 1)
end
return numStr
end
local function getISODate(datestr, datetype, lang, num, case)
-- translate dates in the format YYYY, YYYY-MM, and YYYY-MM-DD
if not case and i18n.Translations[datetype] then
-- look up the grammatical case needed and call ISOdate module
local rec = langSwitch(i18n.Translations[datetype], lang)
if type(rec)=='table' then
case = rec.case[num]
end
end
return ISOdate(datestr, lang, case, '', 1)
end
local function translatePhrase(date1, date2, operation, lang, state)
-- use tables in Module:i18n/complex date to translate a phrase
if not i18n.Translations[operation] then
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: input parameter "%s" is not recognized.</span>', operation or 'nil')
return ''
end
local dateStr = langSwitch(i18n.Translations[operation], lang)
if type(dateStr)=='table' then
dateStr = dateStr[1]
end
if type(dateStr)=='function' then
local success
local nDates = i18n.Translations[operation]['nDates']
if nDates==2 then -- 2 date phrase
success, dateStr = pcall(dateStr, date1, date2, state)
else -- 1 date phrase
success, dateStr = pcall(dateStr, date1, state)
end
end
if type(dateStr)=='string' then
-- replace parts of the string '$date1' and '$date2' with date1 and date2 strings
dateStr = mw.ustring.gsub(dateStr, '$date1', date1)
dateStr = mw.ustring.gsub(dateStr, '$date2', date2)
else
-- Special case of more complex phrases that can be build out of simple phrases
-- If complex case is not translated to "lang" than build it out of simpler ones
local x = dateStr
dateStr = p._complex_date(x.conj, x.adj1, date1, x.units1, x.era1, x.adj2, date2, x.units2, x.era2, lang, 2)
end
return dateStr
end
local function oneDatePhrase(dateStr, adj, era, units, lang, num, case, state)
-- translate a single date phrase
if num==2 then
state.adj, state.era, state.units, state.precision = state.adj2, state.era2, state.units2, state.precision2
end
-- dateStr can have many forms: ISO date, year or a number for
-- decade, century or millennium
if units == '' then -- unit is "year", "month", "day"
dateStr = getISODate(dateStr, adj, lang, num, case)
else -- units is "decade", "century", "millennium''
dateStr = translatePhrase(dateStr, '', units, lang, state)
end
-- add adjective ("early", "mid", etc.) or preposition ("before", "after",
-- "circa", etc.) to the date
if adj ~= '' then
dateStr = translatePhrase(dateStr, '', adj, lang, state)
else -- only era?
dateStr = formatnum1(dateStr, lang)
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function twoDatePhrase(date1, date2, state, lang)
-- translate a double date phrase
local dateStr, case
local era=''
if state.era1 == state.era2 then
-- if both eras are the same than add it only once
era = state.era1
state.era1 = ''
state.era2 = ''
end
case = {nil, nil}
if i18n.Translations[state.conj] then
local rec = langSwitch(i18n.Translations[state.conj], lang)
if type(rec)=='table' then
case = rec.case
end
end
date1 = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, case[1], state)
date2 = oneDatePhrase(date2, state.adj2, state.era2, state.units2, lang, 2, case[2], state)
dateStr = translatePhrase(date1, date2, state.conj, lang, state)
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function otherPhrases(date1, date2, operation, era, lang, state)
-- translate specialized phrases
local dateStr = ''
if operation == 'islamic' then
if date2=='' then date2 = mw.getCurrentFrame():callParserFunction('#time', 'xmY', date1) end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
if era == '' then era = 'ad' end
dateStr = translatePhrase(date1, '', era, lang, state) .. ' (' .. translatePhrase(date2, '', 'ah', lang, state) .. ')'
era = ''
elseif operation == 'julian' then
if not date2 and date1 then -- Convert from Julian to Gregorian calendar date
if Calendar == nil then
Calendar = require("Module:Calendar")
end
local JDN = Calendar._date2jdn(date1, 0)
if JDN then
date2 = date1 -- first date is assumed to be Julian
date1 = Calendar._jdn2date(JDN, 1)
end
end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
dateStr = translatePhrase(date1, date2, operation, lang, state)
dateStr = mw.ustring.gsub(mw.ustring.gsub(dateStr, '%( ', '('), ' %)', ')') -- in case date2 is empty
elseif operation == 'turn of the year' or operation == 'turn of the decade' or operation == 'turn of the century' then
local dt = 1
if operation == 'turn of the decade' then dt=10 end
if not date2 or date2=='' then date2=tostring(tonumber(date1)-dt) end
if era~='bp' and era~='bc' then date1, date2 = date2, date1 end
if operation == 'turn of the year' then
date1 = ISOdate(date1, lang, '', '', 1)
date2 = ISOdate(date2, lang, '', '', 1)
else
date1 = formatnum1(date1, lang)
date2 = formatnum1(date2, lang)
end
dateStr = translatePhrase(date1, date2, operation, lang, state)
elseif operation == 'year unknown' then
dateStr = translatePhrase('', '', operation, lang, state)
elseif operation == 'unknown' then
dateStr = tostring(mw.message.new( "exif-unknowndate" ):inLanguage( lang ))
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
local function checkAliases(str1, str2, sType)
-- some inputs have many aliases - reconcile them and ensure string is playing a proper role
local out = ''
if str1 and str1~='' then
local a = i18n.Synonyms[str1] -- look up synonyms of "str1"
if a then
out = a[1]
else
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: %s is not recognized.</span>', str1)
end
elseif str2 and str2~='' then -- if "str1" of type "sType" is empty than maybe ...
local a = i18n.Synonyms[str2] -- ..."str2" is of the same type and is not empty
if a and a[2]==sType then
out = a[1]
str2 = ''
end
end
return out, str2
end
local function datePrecision(dateStr, units)
-- "in this module "Units" is a string like millennium, century, or decade
-- "precision" is wikibase compatible date precision number: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- based on string or numeric input calculate "Units" and "precision"
local dateNum = tonumber(dateStr) -- TODO dateNum is not used: should remove?
local precision
if type(units)=='number' then
precision = units
if precision>11 then precision=11 end -- clip the range of precision values
if precision==6 then units='millennium'
elseif precision==7 then units='century'
elseif precision==8 then units='decade'
else units = ''
end
elseif type(units)=='string' then
units = string.lower(units);
if units=='millennium' then precision=6
elseif units=='century' then precision=7
elseif units=='decade' then precision=8
else precision=9
end
end
if units=='' or precision==9 then
local sLen = mw.ustring.len(dateStr)
if sLen<= 4 then precision=9
elseif sLen== 7 then precision=10
elseif sLen>=10 then precision=11
end
units=''
end
if precision==6 and dateStr.match( dateStr, '%d000' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/1000) +1)
elseif precision==7 and mw.ustring.match( dateStr, '%d%d00' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/100) +1)
end
return dateStr, units, precision
end
local function isodate2timestamp(dateStr, precision, era)
-- convert date string to timestamps used by Quick Statements
local tStamp = nil
if era == 'ah' or precision<6 then
return nil
elseif era ~= '' then
local eraLUT = {ad='+', bc='-', bp='-' }
era = eraLUT[era]
else
era='+'
end
-- convert isodate to timestamp used by quick statements
if precision>=9 then
if string.match(dateStr,"^%d%d%d%d$") then -- if YYYY format
tStamp = era .. dateStr .. '-00-00T00:00:00Z/9'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d$") then -- if YYYY-MM format
tStamp = era .. dateStr .. '-00T00:00:00Z/10'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d%-%d%d$") then -- if YYYY-MM-DD format
tStamp = era .. dateStr .. 'T00:00:00Z/11'
end
elseif precision==8 then -- decade
tStamp = era .. dateStr .. '-00-00T00:00:00Z/8'
elseif precision==7 then -- century
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '50-00-00T00:00:00Z/7'
elseif precision==6 then
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '500-00-00T00:00:00Z/6'
end
return tStamp
end
local function oneDateQScode(dateStr, adj, era, precision)
-- create QuickStatements string for "one date" dates
local outputStr = ''
local d = isodate2timestamp(dateStr, precision, era)
if not d then
return ''
end
local rLUT = { early='Q40719727' , mid='Q40719748', late='Q40719766',
['1quarter']='Q40690303' , ['2quarter']='Q40719649' , ['3quarter']='Q40719662', ['4quarter']='Q40719674',
spring='Q40720559' , summer='Q40720564' , autumn='Q40720568' , winter='Q40720553',
firsthalf='Q40719687', secondhalf='Q40719707' }
local qLUT = {['from']='P580', ['until']='P582', ['after']='P1319', ['before']='P1326'}
local refine = rLUT[adj]
local qualitier = qLUT[adj]
if adj=='' then
outputStr = d
elseif adj=='circa' then
outputStr = d..",P1480,Q5727902"
elseif refine then
outputStr = d..",P4241,"..refine
elseif precision>7 and qualitier then
local century = string.gsub(d, 'Z%/%d+', 'Z/7')
outputStr = century ..",".. qualitier ..","..d
end
return outputStr
end
local function twoDateQScode(date1, date2, state)
-- create QuickStatements string for "two date" dates
if state.adj1~='' or state.adj2~='' or state.era1~=state.era2 then
return ''
end
local outputStr = ''
local d1 = isodate2timestamp(date1, state.precision1, state.era1)
local d2 = isodate2timestamp(date2, state.precision2, state.era2)
if (not d1) or (not d2) then
return ''
end
-- find date with lower precision in common to both dates
local cd
local year1 = string.sub(d1,2,5)
local k = 0
for i = 1,10,1 do
if string.sub(d1,1,i)==string.sub(d2,1,i) then
k = i -- find last matching letter
end
end
if k>=9 then -- same month, since "+YYYY-MM-" is in common
cd = isodate2timestamp(string.sub(d1,2,8), 10, state.era1)
elseif k>=6 and k<9 then -- same year, since "+YYYY-" is in common
cd = isodate2timestamp(year1, 9, state.era1)
elseif k==4 then -- same decade(k=4, precision=8), since "+YYY" is in common
cd = isodate2timestamp(year1, 8, state.era1)
elseif k==3 then -- same century(k=3, precision=7) since "+YY" is in common
local d = tostring(math.floor(tonumber(year1)/100) +1) -- convert 1999 -> 20
cd = isodate2timestamp( d, 7, state.era1)
elseif k==2 then -- same millennium (k=2, precision=6), since "+Y" is in common
local d = tostring(math.floor(tonumber(year1)/1000) +1) -- convert 1999 -> 2
cd = isodate2timestamp( d, 6, state.era1)
else
return ''
end
--if not cd then
-- return ' <br/>error: ' .. d1.." / " .. d2.." / ".. (cd or '') .." / ".. string.sub(d1,2,5).." / " .. string.sub(d2,2,5).." / " .. tostring(k)
--end
--
if state.conj=='from-until' then
outputStr = cd ..",P580,".. d1 ..",P582,".. d2
elseif state.conj=='between' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2
elseif state.conj=='circa2' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2 ..",P1480,Q5727902"
end
return outputStr
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
function p.Era(frame)
-- process inputs
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local lang = args['lang']
local dateStr = args['date'] or ''
local eraType = string.lower(args['era'] or '')
dateStr = ISOdate(dateStr, lang, '', '', 1)
if eraType then
eraType = checkAliases(eraType ,'','e')
dateStr = translatePhrase(dateStr, '', eraType, lang, {})
end
return dateStr
end
function p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
local Output=''
-- process inputs and save date in state array
local state = {}
state.conj = string.lower(conj or '')
state.adj1 = string.lower(adj1 or '')
state.adj2 = string.lower(adj2 or '')
state.era1 = string.lower(era1 or '')
state.era2 = string.lower(era2 or '')
state.units1 = string.lower(units1 or '')
state.units2 = string.lower(units2 or '')
-- if date 1 is missing but date 2 is provided than swap them
if date1 == '' and date2 ~= '' then
date1 = date2
date2 = ''
state = {adj1 = state.adj2, era1 = state.era2, units1 = state.units2,
adj2 = '', era2 = '', units2 = '', conj=state.conj, num=1}
end
if date2 ~= '' then state.nDates = 2
elseif date1 ~= '' then state.nDates = 1
else state.nDates = 0
end
-- reconcile alternative names for text inputs
local conj = checkAliases(state.conj ,'' ,'j')
state.adj1 ,conj = checkAliases(state.adj1 ,conj,'a')
state.units1,conj = checkAliases(state.units1,conj,'p')
state.era1 ,conj = checkAliases(state.era1 ,conj,'e')
state.special,conj = checkAliases('',conj,'c')
state.adj2 = checkAliases(state.adj2 ,'','a')
state.units2 = checkAliases(state.units2,'','p')
state.era2 = checkAliases(state.era2 ,'','e')
state.conj = conj
state.lang = lang
if p.Error~=nil then
return nil
end
-- calculate date precision value
date1, state.units1, state.precision1 = datePrecision(date1, state.units1)
date2, state.units2, state.precision2 = datePrecision(date2, state.units2)
-- Handle special cases
-- Some complex phrases can be created out of simpler ones. Therefore on pass # 1 we try to create
-- the phrase using complex phrase and if that is not found than on the second pass we try to build
-- the phrase out of the simpler ones
if passNr==1 then
if state.adj1=='circa' and state.nDates == 2 then
state.conj = 'circa2'
state.adj1 = ''
state.adj2 = ''
end
if state.nDates == 2 and state.adj1=='late' and state.adj2=='early' and state.conj=='and'
and state.units1==state.units2 and state.era1==state.era2 then
if state.units1=='century' then
state.conj='turn of the century'
elseif state.units1=='decade' then
state.conj='turn of the decade'
elseif state.units1=='' then
state.conj='turn of the year'
end
state.adj1 = ''
state.adj2 = ''
state.units1 = ''
state.units2 = ''
end
end
local errorStr = string.format(
'\n*conj=%s, adj1=%s, era1=%s, unit1=%s, prec1=%i, adj2=%s, era2=%s, unit2=%s, prec2=%i, special=%s',
state.conj, state.adj1, state.era1, state.units1, state.precision1,
state.adj2, state.era2, state.units2, state.precision2, state.special)
state.adj, state.era, state.units, state.precision = state.adj1, state.era1, state.units1, state.precision1
-- call specialized functions
local QScode = ''
if state.special~='' then
Output = otherPhrases(date1, date2, state.special, state.era1, lang, state)
elseif state.conj~='' then
QScode = twoDateQScode(date1, date2, state)
Output = twoDatePhrase(date1, date2, state, lang)
elseif state.adj1~='' or state.era1~='' or state.units1~='' then
Output = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, nil, state)
QScode = oneDateQScode(date1, state.adj1, state.era1, state.precision1)
elseif date1~='' then
Output = ISOdate(date1, lang, '', 'dtstart', '100-999')
end
if p.Error~=nil then
return errorStr
end
-- if there is any wikicode in the string than execute it
if mw.ustring.find(Output, '{') then
Output = mw.getCurrentFrame():preprocess(Output)
end
if QScode and #QScode>0 then
QScode = ' <div style="display: none;">date QS:P,' .. QScode .. '</div>'
end
return Output .. QScode
end
function p.complex_date(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local date1 = args['date1'] or args['2'] or args['date'] or ''
local date2 = args['date2'] or args['3'] or ''
local conj = args['conj'] or args['1'] or ''
local adj1 = args['adj1'] or args['adj'] or ''
local adj2 = args['adj2'] or ''
local units1 = args['precision1'] or args['precision'] or ''
local units2 = args['precision2'] or args['precision'] or ''
local era1 = args['era1'] or args['era'] or ''
local era2 = args['era2'] or args['era'] or ''
local lang = args['lang']
dateStr = p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
if p.Error~=nil then
dateStr = p.Error .. '[[Category:Pages using Complex date template with incorrect parameter]]'
end
return dateStr
end
return p
fhn8afhakgi5qa5gxem0fsfqnz0xl2s
803633
803632
2020-05-15T19:21:05Z
en>RexxS
0
update from sandbox (commons version)
803633
Scribunto
text/plain
--[[
__ __ _ _ ____ _ _
| \/ | ___ __| |_ _| | ___ _ / ___|___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_) | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_| |__| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)\____\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This module is intended for creation of complex date phrases in variety of languages.
Once deployed, please do not modify this code without applying the changes first at Module:Complex date/sandbox and testing
at Module:Complex date/sandbox/testcases.
Authors and maintainers:
* User:Sn1per - first draft of the original version
* User:Jarekt - corrections and expansion of the original version
]]
-- List of external modules and functions
local p = {Error = nil}
local i18n = require('Module:i18n/complex date') -- used for translations of date related phrases
local ISOdate = require('Module:ISOdate')._ISOdate -- used for parsing dates in YYYY-MM-DD and related formats
local Calendar -- loaded lazily
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
table.insert(langList,math.max(#langList,2),'default')
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
-- ==================================================
local function formatnum1(numStr, lang)
-- mostly require('Module:Formatnum').formatNum function used to translate a number to use different numeral characters,
-- except that it it does not call that function unless the language is on the list "LList"
local LList = {bn=1,bpy=1,kn=1,hi=1,mr=1,new=1,pa=1,gu=1,fa=1,glk=1,mzn=1,ur=1,ar=1,ckb=1,ks=1,lo=1,['or']=1,bo=1,['ml-old']=1,mn=1,te=1,th=1}
if LList[lang] then -- call only when the language is on the list
numStr = require('Module:Formatnum').formatNum(numStr, lang, 1)
end
return numStr
end
-- ==================================================
local function getISODate(datestr, datetype, lang, num, case)
-- translate dates in the format YYYY, YYYY-MM, and YYYY-MM-DD
if not case and i18n.Translations[datetype] then
-- look up the grammatical case needed and call ISOdate module
local rec = langSwitch(i18n.Translations[datetype], lang)
if type(rec)=='table' then
case = rec.case[num]
end
end
return ISOdate(datestr, lang, case, '', 1)
end
-- =======================================================================
local function translatePhrase(date1, date2, operation, lang, state)
-- use tables in Module:i18n/complex date to translate a phrase
if not i18n.Translations[operation] then
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: input parameter "%s" is not recognized.</span>', operation or 'nil')
return ''
end
local dateStr = langSwitch(i18n.Translations[operation], lang)
if type(dateStr)=='table' then
dateStr = dateStr[1]
end
if type(dateStr)=='function' then
local dateFunc = dateStr
local nDates = i18n.Translations[operation]['nDates']
if nDates==2 then -- 2 date phrase
dateStr = dateFunc(date1, date2, state)
else -- 1 date phrase
dateStr = dateFunc(date1, state)
end
end
if type(dateStr)=='string' then
-- replace parts of the string '$date1' and '$date2' with date1 and date2 strings
dateStr = mw.ustring.gsub(dateStr, '$date1', date1)
dateStr = mw.ustring.gsub(dateStr, '$date2', date2)
else
-- Special case of more complex phrases that can be build out of simple phrases
-- If complex case is not translated to "lang" than build it out of simpler ones
local x = dateStr
dateStr = p._complex_date(x.conj, x.adj1, date1, x.units1, x.era1, x.adj2, date2, x.units2, x.era2, lang, 2)
end
return dateStr
end
-- =======================================================================
local function oneDatePhrase(dateStr, adj, era, units, lang, num, case, state)
-- translate a single date phrase
if num==2 then
state.adj, state.era, state.units, state.precision = state.adj2, state.era2, state.units2, state.precision2
end
-- dateStr can have many forms: ISO date, year or a number for
-- decade, century or millennium
if units == '' then -- unit is "year", "month", "day"
dateStr = getISODate(dateStr, adj, lang, num, case)
else -- units is "decade", "century", "millennium''
dateStr = translatePhrase(dateStr, '', units, lang, state)
end
-- add adjective ("early", "mid", etc.) or preposition ("before", "after",
-- "circa", etc.) to the date
if adj ~= '' then
dateStr = translatePhrase(dateStr, '', adj, lang, state)
else -- only era?
dateStr = formatnum1(dateStr, lang)
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
-- =======================================================================
local function twoDatePhrase(date1, date2, state, lang)
-- translate a double date phrase
local dateStr, case
local era=''
if state.era1 == state.era2 then
-- if both eras are the same than add it only once
era = state.era1
state.era1 = ''
state.era2 = ''
end
case = {nil, nil}
if i18n.Translations[state.conj] then
local rec = langSwitch(i18n.Translations[state.conj], lang)
if type(rec)=='table' then
case = rec.case
end
end
date1 = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, case[1], state)
date2 = oneDatePhrase(date2, state.adj2, state.era2, state.units2, lang, 2, case[2], state)
dateStr = translatePhrase(date1, date2, state.conj, lang, state)
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
-- =======================================================================
local function otherPhrases(date1, date2, operation, era, lang, state)
-- translate specialized phrases
local dateStr = ''
if operation == 'islamic' then
if date2=='' then date2 = mw.getCurrentFrame():callParserFunction('#time', 'xmY', date1) end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
if era == '' then era = 'ad' end
dateStr = translatePhrase(date1, '', era, lang, state) .. ' (' .. translatePhrase(date2, '', 'ah', lang, state) .. ')'
era = ''
elseif operation == 'julian' then
if not date2 and date1 then -- Convert from Julian to Gregorian calendar date
if Calendar == nil then
Calendar = require("Module:Calendar") -- lazy loding (only if needed)
end
local JDN = Calendar._date2jdn(date1, 0)
if JDN then
date2 = date1 -- first date is assumed to be Julian
date1 = Calendar._jdn2date(JDN, 1)
end
end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
dateStr = translatePhrase(date1, date2, operation, lang, state)
dateStr = mw.ustring.gsub(mw.ustring.gsub(dateStr, '%( ', '('), ' %)', ')') -- in case date2 is empty
elseif operation == 'turn of the year' or operation == 'turn of the decade' or operation == 'turn of the century' then
local dt = 1
if operation == 'turn of the decade' then dt=10 end
if not date2 or date2=='' then date2=tostring(tonumber(date1)-dt) end
if era~='bp' and era~='bc' then date1, date2 = date2, date1 end
if operation == 'turn of the year' then
date1 = ISOdate(date1, lang, '', '', 1)
date2 = ISOdate(date2, lang, '', '', 1)
else
date1 = formatnum1(date1, lang)
date2 = formatnum1(date2, lang)
end
dateStr = translatePhrase(date1, date2, operation, lang, state)
elseif operation == 'year unknown' then
dateStr = translatePhrase('', '', operation, lang, state) .. '<div style="display: none;">Unknown date</div>'
elseif operation == 'unknown' then
dateStr = tostring(mw.message.new( "exif-unknowndate" ):inLanguage( lang )) .. '<div style="display: none;">Unknown date</div>'
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
-- =======================================================================
local function checkAliases(str1, str2, sType)
-- some inputs have many aliases - reconcile them and ensure string is playing a proper role
local out = ''
if str1 and str1~='' then
local a = i18n.Synonyms[str1] -- look up synonyms of "str1"
if a then
out = a[1]
else
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: %s is not recognized.</span>', str1)
end
elseif str2 and str2~='' then -- if "str1" of type "sType" is empty than maybe ...
local a = i18n.Synonyms[str2] -- ..."str2" is of the same type and is not empty
if a and a[2]==sType then
out = a[1]
str2 = ''
end
end
return out, str2
end
-- =======================================================================
local function datePrecision(dateStr, units)
-- "in this module "Units" is a string like millennium, century, or decade
-- "precision" is wikibase compatible date precision number: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- based on string or numeric input calculate "Units" and "precision"
local precision
if type(units)=='number' then
precision = units
if precision>11 then precision=11 end -- clip the range of precision values
if precision==6 then units='millennium'
elseif precision==7 then units='century'
elseif precision==8 then units='decade'
else units = ''
end
elseif type(units)=='string' then
units = string.lower(units)
if units=='millennium' then precision=6
elseif units=='century' then precision=7
elseif units=='decade' then precision=8
else precision=9
end
end
if units=='' or precision==9 then
local sLen = mw.ustring.len(dateStr)
if sLen<= 4 then precision=9
elseif sLen== 7 then precision=10
elseif sLen>=10 then precision=11
end
units=''
end
if precision==6 and dateStr.match( dateStr, '%d000' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/1000) +1)
elseif precision==7 and mw.ustring.match( dateStr, '%d%d00' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/100) +1)
end
return dateStr, units, precision
end
-- =======================================================================
local function isodate2timestamp(dateStr, precision, era)
-- convert date string to timestamps used by Quick Statements
local tStamp = nil
if era == 'ah' or precision<6 then
return nil
elseif era ~= '' then
local eraLUT = {ad='+', bc='-', bp='-' }
era = eraLUT[era]
else
era='+'
end
-- convert isodate to timestamp used by quick statements
if precision>=9 then
if string.match(dateStr,"^%d%d%d%d$") then -- if YYYY format
tStamp = era .. dateStr .. '-00-00T00:00:00Z/9'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d$") then -- if YYYY-MM format
tStamp = era .. dateStr .. '-00T00:00:00Z/10'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d%-%d%d$") then -- if YYYY-MM-DD format
tStamp = era .. dateStr .. 'T00:00:00Z/11'
end
elseif precision==8 then -- decade
tStamp = era .. dateStr .. '-00-00T00:00:00Z/8'
elseif precision==7 then -- century
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '50-00-00T00:00:00Z/7'
elseif precision==6 then
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '500-00-00T00:00:00Z/6'
end
return tStamp
end
-- =======================================================================
local function oneDateQScode(dateStr, adj, era, precision)
-- create QuickStatements string for "one date" dates
local outputStr = ''
local d = isodate2timestamp(dateStr, precision, era)
if not d then
return ''
end
local rLUT = { early='Q40719727' , mid='Q40719748', late='Q40719766',
['1quarter']='Q40690303' , ['2quarter']='Q40719649' , ['3quarter']='Q40719662', ['4quarter']='Q40719674',
spring='Q40720559' , summer='Q40720564' , autumn='Q40720568' , winter='Q40720553',
firsthalf='Q40719687', secondhalf='Q40719707' }
local qLUT = {['from']='P580', ['until']='P582', ['after']='P1319', ['before']='P1326', ['by']='P1326'}
local refine = rLUT[adj]
local qualitier = qLUT[adj]
if adj=='' then
outputStr = d
elseif adj=='circa' then
outputStr = d..",P1480,Q5727902"
elseif refine then
outputStr = d..",P4241,"..refine
elseif precision>7 and qualitier then
local century = string.gsub(d, 'Z%/%d+', 'Z/7')
outputStr = century ..",".. qualitier ..","..d
end
return outputStr
end
-- =======================================================================
local function twoDateQScode(date1, date2, state)
-- create QuickStatements string for "two date" dates
if state.adj1~='' or state.adj2~='' or state.era1~=state.era2 then
return '' -- QuickStatements string are not generated for two date phrases with adjectives
end
local outputStr = ''
local d1 = isodate2timestamp(date1, state.precision1, state.era1)
local d2 = isodate2timestamp(date2, state.precision2, state.era2)
if (not d1) or (not d2) then
return ''
end
-- find date with lower precision in common to both dates
local cd
local year1 = tonumber(string.sub(d1,2,5))
local year2 = tonumber(string.sub(d2,2,5))
local k = 0
for i = 1,10,1 do
if string.sub(d1,1,i)==string.sub(d2,1,i) then
k = i -- find last matching letter
end
end
if k>=9 then -- same month, since "+YYYY-MM-" is in common
cd = isodate2timestamp(string.sub(d1,2,8), 10, state.era1)
elseif k>=6 and k<9 then -- same year, since "+YYYY-" is in common
cd = isodate2timestamp(tostring(year1), 9, state.era1)
elseif k==4 then -- same decade(k=4, precision=8), since "+YYY" is in common
cd = isodate2timestamp(tostring(year1), 8, state.era1)
elseif k==3 then -- same century(k=3, precision=7) since "+YY" is in common
local d = tostring(math.floor(year1/100) +1) -- convert 1999 -> 20
cd = isodate2timestamp( d, 7, state.era1)
elseif k==2 then -- same millennium (k=2, precision=6), since "+Y" is in common
local d = tostring(math.floor(year1/1000) +1) -- convert 1999 -> 2
cd = isodate2timestamp( d, 6, state.era1)
end
if not cd then
return ''
end
--if not cd then
-- return ' <br/>error: ' .. d1.." / " .. d2.." / ".. (cd or '') .." / ".. string.sub(d1,2,5).." / " .. string.sub(d2,2,5).." / " .. tostring(k)
--end
--
if (state.conj=='from-until') or (state.conj=='and' and year1==year2-1) then
outputStr = cd ..",P580,".. d1 ..",P582,".. d2
elseif (state.conj=='between') or (state.conj=='or' and year1==year2-1) then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2
elseif state.conj=='circa2' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2 ..",P1480,Q5727902"
end
return outputStr
end
-- =======================================================================
local function processInputParams(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
-- process inputs and save date in state array
local state = {}
state.conj = string.lower(conj or '')
state.adj1 = string.lower(adj1 or '')
state.adj2 = string.lower(adj2 or '')
state.era1 = string.lower(era1 or '')
state.era2 = string.lower(era2 or '')
state.units1 = string.lower(units1 or '')
state.units2 = string.lower(units2 or '')
-- if date 1 is missing but date 2 is provided than swap them
if date1 == '' and date2 ~= '' then
date1 = date2
date2 = ''
state = {adj1 = state.adj2, era1 = state.era2, units1 = state.units2,
adj2 = '', era2 = '', units2 = '', conj=state.conj, num=1}
end
if date2 ~= '' then state.nDates = 2
elseif date1 ~= '' then state.nDates = 1
else state.nDates = 0
end
-- reconcile alternative names for text inputs
local conj = checkAliases(state.conj ,'' ,'j')
state.adj1 ,conj = checkAliases(state.adj1 ,conj,'a')
state.units1,conj = checkAliases(state.units1,conj,'p')
state.era1 ,conj = checkAliases(state.era1 ,conj,'e')
state.special,conj = checkAliases('',conj,'c')
state.adj2 = checkAliases(state.adj2 ,'','a')
state.units2 = checkAliases(state.units2,'','p')
state.era2 = checkAliases(state.era2 ,'','e')
state.conj = conj
state.lang = lang
if p.Error~=nil then
return nil
end
-- calculate date precision value
date1, state.units1, state.precision1 = datePrecision(date1, state.units1)
date2, state.units2, state.precision2 = datePrecision(date2, state.units2)
-- Handle special cases
-- Some complex phrases can be created out of simpler ones. Therefore on pass # 1 we try to create
-- the phrase using complex phrase and if that is not found than on the second pass we try to build
-- the phrase out of the simpler ones
if passNr==1 then
if state.adj1=='circa' and state.nDates == 2 then
state.conj = 'circa2'
state.adj1 = ''
state.adj2 = ''
end
if state.nDates == 2 and state.adj1=='late' and state.adj2=='early' and state.conj=='and'
and state.units1==state.units2 and state.era1==state.era2 then
if state.units1=='century' then
state.conj='turn of the century'
elseif state.units1=='decade' then
state.conj='turn of the decade'
elseif state.units1=='' then
state.conj='turn of the year'
end
state.adj1 = ''
state.adj2 = ''
state.units1 = ''
state.units2 = ''
end
end
state.adj, state.era, state.units, state.precision = state.adj1, state.era1, state.units1, state.precision1
return date1, date2, state
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
function p.Era(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local lang = args['lang']
local dateStr = args['date'] or ''
local eraType = string.lower(args['era'] or '')
dateStr = ISOdate(dateStr, lang, '', '', 1)
if eraType then
eraType = checkAliases(eraType ,'','e')
dateStr = translatePhrase(dateStr, '', eraType, lang, {})
end
return dateStr
end
-- =======================================================================
function p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
local Output=''
local state
-- process inputs and save date in state array
date1, date2, state = processInputParams(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
if p.Error~=nil then
return nil
end
local errorStr = string.format(
'\n*conj=%s, adj1=%s, era1=%s, unit1=%s, prec1=%i, adj2=%s, era2=%s, unit2=%s, prec2=%i, special=%s',
state.conj, state.adj1, state.era1, state.units1, state.precision1,
state.adj2, state.era2, state.units2, state.precision2, state.special)
-- call specialized functions
local QScode = ''
if state.special~='' then
Output = otherPhrases(date1, date2, state.special, state.era1, lang, state)
elseif state.conj~='' then
QScode = twoDateQScode(date1, date2, state)
Output = twoDatePhrase(date1, date2, state, lang)
elseif state.adj1~='' or state.era1~='' or state.units1~='' then
Output = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, nil, state)
QScode = oneDateQScode(date1, state.adj1, state.era1, state.precision1)
elseif date1~='' then
Output = ISOdate(date1, lang, '', 'dtstart', '100-999')
end
if p.Error~=nil then
return errorStr
end
-- if there is any wikicode in the string than execute it
if mw.ustring.find(Output, '{') then
Output = mw.getCurrentFrame():preprocess(Output)
end
if QScode and #QScode>0 then
QScode = ' <div style="display: none;">date QS:P,' .. QScode .. '</div>'
end
return Output .. QScode
end
-- =======================================================================
function p._complex_date_cer(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, certainty, lang)
-- same as p._complex_date but with extra parameter for certainty: probably, possibly, presumably, etc.
local dateStr = p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
certainty = checkAliases(certainty, conj, 'r')
local LUT = {probably='Q56644435', presumably='Q18122778', possibly='Q30230067', circa='Q5727902' }
if certainty and LUT[certainty] then
local state = {}
date1, date2, state = processInputParams(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
dateStr = translatePhrase(dateStr, '', certainty, lang, state)
dateStr = string.gsub(dateStr, '(%<div style="display: none;"%>date QS:P,[^%<]+)(%</div%>)', '%1,P1480,' .. LUT[certainty] .. '%2' )
end
return dateStr
end
-- =======================================================================
function p.complex_date(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local date1 = args['date1'] or args['2'] or args['date'] or ''
local date2 = args['date2'] or args['3'] or ''
local conj = args['conj'] or args['1'] or ''
local adj1 = args['adj1'] or args['adj'] or ''
local adj2 = args['adj2'] or ''
local units1 = args['precision1'] or args['precision'] or ''
local units2 = args['precision2'] or args['precision'] or ''
local era1 = args['era1'] or args['era'] or ''
local era2 = args['era2'] or args['era'] or ''
local certainty = args['certainty']
local lang = args['lang']
dateStr = p._complex_date_cer(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, certainty, lang)
if p.Error~=nil then
dateStr = p.Error .. '[[Category:Pages using Complex date template with incorrect parameter]]'
end
return dateStr
end
return p
ppv1t08ydt8a9x36u2iltdsomq7mcr5
803634
803633
2026-07-30T00:20:17Z
SM7
3953
9 revisions imported from [[:en:Module:Complex_date]]
803633
Scribunto
text/plain
--[[
__ __ _ _ ____ _ _
| \/ | ___ __| |_ _| | ___ _ / ___|___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_) | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_| |__| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)\____\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This module is intended for creation of complex date phrases in variety of languages.
Once deployed, please do not modify this code without applying the changes first at Module:Complex date/sandbox and testing
at Module:Complex date/sandbox/testcases.
Authors and maintainers:
* User:Sn1per - first draft of the original version
* User:Jarekt - corrections and expansion of the original version
]]
-- List of external modules and functions
local p = {Error = nil}
local i18n = require('Module:i18n/complex date') -- used for translations of date related phrases
local ISOdate = require('Module:ISOdate')._ISOdate -- used for parsing dates in YYYY-MM-DD and related formats
local Calendar -- loaded lazily
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
table.insert(langList,math.max(#langList,2),'default')
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
-- ==================================================
local function formatnum1(numStr, lang)
-- mostly require('Module:Formatnum').formatNum function used to translate a number to use different numeral characters,
-- except that it it does not call that function unless the language is on the list "LList"
local LList = {bn=1,bpy=1,kn=1,hi=1,mr=1,new=1,pa=1,gu=1,fa=1,glk=1,mzn=1,ur=1,ar=1,ckb=1,ks=1,lo=1,['or']=1,bo=1,['ml-old']=1,mn=1,te=1,th=1}
if LList[lang] then -- call only when the language is on the list
numStr = require('Module:Formatnum').formatNum(numStr, lang, 1)
end
return numStr
end
-- ==================================================
local function getISODate(datestr, datetype, lang, num, case)
-- translate dates in the format YYYY, YYYY-MM, and YYYY-MM-DD
if not case and i18n.Translations[datetype] then
-- look up the grammatical case needed and call ISOdate module
local rec = langSwitch(i18n.Translations[datetype], lang)
if type(rec)=='table' then
case = rec.case[num]
end
end
return ISOdate(datestr, lang, case, '', 1)
end
-- =======================================================================
local function translatePhrase(date1, date2, operation, lang, state)
-- use tables in Module:i18n/complex date to translate a phrase
if not i18n.Translations[operation] then
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: input parameter "%s" is not recognized.</span>', operation or 'nil')
return ''
end
local dateStr = langSwitch(i18n.Translations[operation], lang)
if type(dateStr)=='table' then
dateStr = dateStr[1]
end
if type(dateStr)=='function' then
local dateFunc = dateStr
local nDates = i18n.Translations[operation]['nDates']
if nDates==2 then -- 2 date phrase
dateStr = dateFunc(date1, date2, state)
else -- 1 date phrase
dateStr = dateFunc(date1, state)
end
end
if type(dateStr)=='string' then
-- replace parts of the string '$date1' and '$date2' with date1 and date2 strings
dateStr = mw.ustring.gsub(dateStr, '$date1', date1)
dateStr = mw.ustring.gsub(dateStr, '$date2', date2)
else
-- Special case of more complex phrases that can be build out of simple phrases
-- If complex case is not translated to "lang" than build it out of simpler ones
local x = dateStr
dateStr = p._complex_date(x.conj, x.adj1, date1, x.units1, x.era1, x.adj2, date2, x.units2, x.era2, lang, 2)
end
return dateStr
end
-- =======================================================================
local function oneDatePhrase(dateStr, adj, era, units, lang, num, case, state)
-- translate a single date phrase
if num==2 then
state.adj, state.era, state.units, state.precision = state.adj2, state.era2, state.units2, state.precision2
end
-- dateStr can have many forms: ISO date, year or a number for
-- decade, century or millennium
if units == '' then -- unit is "year", "month", "day"
dateStr = getISODate(dateStr, adj, lang, num, case)
else -- units is "decade", "century", "millennium''
dateStr = translatePhrase(dateStr, '', units, lang, state)
end
-- add adjective ("early", "mid", etc.) or preposition ("before", "after",
-- "circa", etc.) to the date
if adj ~= '' then
dateStr = translatePhrase(dateStr, '', adj, lang, state)
else -- only era?
dateStr = formatnum1(dateStr, lang)
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
-- =======================================================================
local function twoDatePhrase(date1, date2, state, lang)
-- translate a double date phrase
local dateStr, case
local era=''
if state.era1 == state.era2 then
-- if both eras are the same than add it only once
era = state.era1
state.era1 = ''
state.era2 = ''
end
case = {nil, nil}
if i18n.Translations[state.conj] then
local rec = langSwitch(i18n.Translations[state.conj], lang)
if type(rec)=='table' then
case = rec.case
end
end
date1 = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, case[1], state)
date2 = oneDatePhrase(date2, state.adj2, state.era2, state.units2, lang, 2, case[2], state)
dateStr = translatePhrase(date1, date2, state.conj, lang, state)
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
-- =======================================================================
local function otherPhrases(date1, date2, operation, era, lang, state)
-- translate specialized phrases
local dateStr = ''
if operation == 'islamic' then
if date2=='' then date2 = mw.getCurrentFrame():callParserFunction('#time', 'xmY', date1) end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
if era == '' then era = 'ad' end
dateStr = translatePhrase(date1, '', era, lang, state) .. ' (' .. translatePhrase(date2, '', 'ah', lang, state) .. ')'
era = ''
elseif operation == 'julian' then
if not date2 and date1 then -- Convert from Julian to Gregorian calendar date
if Calendar == nil then
Calendar = require("Module:Calendar") -- lazy loding (only if needed)
end
local JDN = Calendar._date2jdn(date1, 0)
if JDN then
date2 = date1 -- first date is assumed to be Julian
date1 = Calendar._jdn2date(JDN, 1)
end
end
date1 = getISODate(date1, operation, lang, 1, nil)
date2 = getISODate(date2, operation, lang, 2, nil)
dateStr = translatePhrase(date1, date2, operation, lang, state)
dateStr = mw.ustring.gsub(mw.ustring.gsub(dateStr, '%( ', '('), ' %)', ')') -- in case date2 is empty
elseif operation == 'turn of the year' or operation == 'turn of the decade' or operation == 'turn of the century' then
local dt = 1
if operation == 'turn of the decade' then dt=10 end
if not date2 or date2=='' then date2=tostring(tonumber(date1)-dt) end
if era~='bp' and era~='bc' then date1, date2 = date2, date1 end
if operation == 'turn of the year' then
date1 = ISOdate(date1, lang, '', '', 1)
date2 = ISOdate(date2, lang, '', '', 1)
else
date1 = formatnum1(date1, lang)
date2 = formatnum1(date2, lang)
end
dateStr = translatePhrase(date1, date2, operation, lang, state)
elseif operation == 'year unknown' then
dateStr = translatePhrase('', '', operation, lang, state) .. '<div style="display: none;">Unknown date</div>'
elseif operation == 'unknown' then
dateStr = tostring(mw.message.new( "exif-unknowndate" ):inLanguage( lang )) .. '<div style="display: none;">Unknown date</div>'
end
-- add era
if era ~= '' then
dateStr = translatePhrase(dateStr, '', era, lang, state)
end
return dateStr
end
-- =======================================================================
local function checkAliases(str1, str2, sType)
-- some inputs have many aliases - reconcile them and ensure string is playing a proper role
local out = ''
if str1 and str1~='' then
local a = i18n.Synonyms[str1] -- look up synonyms of "str1"
if a then
out = a[1]
else
p.Error = string.format('<span style="background-color:red;">Error in [[Module:Complex date]]: %s is not recognized.</span>', str1)
end
elseif str2 and str2~='' then -- if "str1" of type "sType" is empty than maybe ...
local a = i18n.Synonyms[str2] -- ..."str2" is of the same type and is not empty
if a and a[2]==sType then
out = a[1]
str2 = ''
end
end
return out, str2
end
-- =======================================================================
local function datePrecision(dateStr, units)
-- "in this module "Units" is a string like millennium, century, or decade
-- "precision" is wikibase compatible date precision number: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- based on string or numeric input calculate "Units" and "precision"
local precision
if type(units)=='number' then
precision = units
if precision>11 then precision=11 end -- clip the range of precision values
if precision==6 then units='millennium'
elseif precision==7 then units='century'
elseif precision==8 then units='decade'
else units = ''
end
elseif type(units)=='string' then
units = string.lower(units)
if units=='millennium' then precision=6
elseif units=='century' then precision=7
elseif units=='decade' then precision=8
else precision=9
end
end
if units=='' or precision==9 then
local sLen = mw.ustring.len(dateStr)
if sLen<= 4 then precision=9
elseif sLen== 7 then precision=10
elseif sLen>=10 then precision=11
end
units=''
end
if precision==6 and dateStr.match( dateStr, '%d000' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/1000) +1)
elseif precision==7 and mw.ustring.match( dateStr, '%d%d00' )~=nil then
dateStr = tostring(math.floor(tonumber(dateStr)/100) +1)
end
return dateStr, units, precision
end
-- =======================================================================
local function isodate2timestamp(dateStr, precision, era)
-- convert date string to timestamps used by Quick Statements
local tStamp = nil
if era == 'ah' or precision<6 then
return nil
elseif era ~= '' then
local eraLUT = {ad='+', bc='-', bp='-' }
era = eraLUT[era]
else
era='+'
end
-- convert isodate to timestamp used by quick statements
if precision>=9 then
if string.match(dateStr,"^%d%d%d%d$") then -- if YYYY format
tStamp = era .. dateStr .. '-00-00T00:00:00Z/9'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d$") then -- if YYYY-MM format
tStamp = era .. dateStr .. '-00T00:00:00Z/10'
elseif string.match(dateStr,"^%d%d%d%d%-%d%d%-%d%d$") then -- if YYYY-MM-DD format
tStamp = era .. dateStr .. 'T00:00:00Z/11'
end
elseif precision==8 then -- decade
tStamp = era .. dateStr .. '-00-00T00:00:00Z/8'
elseif precision==7 then -- century
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '50-00-00T00:00:00Z/7'
elseif precision==6 then
local d = tostring(tonumber(dateStr)-1)
tStamp = era .. d .. '500-00-00T00:00:00Z/6'
end
return tStamp
end
-- =======================================================================
local function oneDateQScode(dateStr, adj, era, precision)
-- create QuickStatements string for "one date" dates
local outputStr = ''
local d = isodate2timestamp(dateStr, precision, era)
if not d then
return ''
end
local rLUT = { early='Q40719727' , mid='Q40719748', late='Q40719766',
['1quarter']='Q40690303' , ['2quarter']='Q40719649' , ['3quarter']='Q40719662', ['4quarter']='Q40719674',
spring='Q40720559' , summer='Q40720564' , autumn='Q40720568' , winter='Q40720553',
firsthalf='Q40719687', secondhalf='Q40719707' }
local qLUT = {['from']='P580', ['until']='P582', ['after']='P1319', ['before']='P1326', ['by']='P1326'}
local refine = rLUT[adj]
local qualitier = qLUT[adj]
if adj=='' then
outputStr = d
elseif adj=='circa' then
outputStr = d..",P1480,Q5727902"
elseif refine then
outputStr = d..",P4241,"..refine
elseif precision>7 and qualitier then
local century = string.gsub(d, 'Z%/%d+', 'Z/7')
outputStr = century ..",".. qualitier ..","..d
end
return outputStr
end
-- =======================================================================
local function twoDateQScode(date1, date2, state)
-- create QuickStatements string for "two date" dates
if state.adj1~='' or state.adj2~='' or state.era1~=state.era2 then
return '' -- QuickStatements string are not generated for two date phrases with adjectives
end
local outputStr = ''
local d1 = isodate2timestamp(date1, state.precision1, state.era1)
local d2 = isodate2timestamp(date2, state.precision2, state.era2)
if (not d1) or (not d2) then
return ''
end
-- find date with lower precision in common to both dates
local cd
local year1 = tonumber(string.sub(d1,2,5))
local year2 = tonumber(string.sub(d2,2,5))
local k = 0
for i = 1,10,1 do
if string.sub(d1,1,i)==string.sub(d2,1,i) then
k = i -- find last matching letter
end
end
if k>=9 then -- same month, since "+YYYY-MM-" is in common
cd = isodate2timestamp(string.sub(d1,2,8), 10, state.era1)
elseif k>=6 and k<9 then -- same year, since "+YYYY-" is in common
cd = isodate2timestamp(tostring(year1), 9, state.era1)
elseif k==4 then -- same decade(k=4, precision=8), since "+YYY" is in common
cd = isodate2timestamp(tostring(year1), 8, state.era1)
elseif k==3 then -- same century(k=3, precision=7) since "+YY" is in common
local d = tostring(math.floor(year1/100) +1) -- convert 1999 -> 20
cd = isodate2timestamp( d, 7, state.era1)
elseif k==2 then -- same millennium (k=2, precision=6), since "+Y" is in common
local d = tostring(math.floor(year1/1000) +1) -- convert 1999 -> 2
cd = isodate2timestamp( d, 6, state.era1)
end
if not cd then
return ''
end
--if not cd then
-- return ' <br/>error: ' .. d1.." / " .. d2.." / ".. (cd or '') .." / ".. string.sub(d1,2,5).." / " .. string.sub(d2,2,5).." / " .. tostring(k)
--end
--
if (state.conj=='from-until') or (state.conj=='and' and year1==year2-1) then
outputStr = cd ..",P580,".. d1 ..",P582,".. d2
elseif (state.conj=='between') or (state.conj=='or' and year1==year2-1) then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2
elseif state.conj=='circa2' then
outputStr = cd ..",P1319,".. d1 ..",P1326,".. d2 ..",P1480,Q5727902"
end
return outputStr
end
-- =======================================================================
local function processInputParams(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
-- process inputs and save date in state array
local state = {}
state.conj = string.lower(conj or '')
state.adj1 = string.lower(adj1 or '')
state.adj2 = string.lower(adj2 or '')
state.era1 = string.lower(era1 or '')
state.era2 = string.lower(era2 or '')
state.units1 = string.lower(units1 or '')
state.units2 = string.lower(units2 or '')
-- if date 1 is missing but date 2 is provided than swap them
if date1 == '' and date2 ~= '' then
date1 = date2
date2 = ''
state = {adj1 = state.adj2, era1 = state.era2, units1 = state.units2,
adj2 = '', era2 = '', units2 = '', conj=state.conj, num=1}
end
if date2 ~= '' then state.nDates = 2
elseif date1 ~= '' then state.nDates = 1
else state.nDates = 0
end
-- reconcile alternative names for text inputs
local conj = checkAliases(state.conj ,'' ,'j')
state.adj1 ,conj = checkAliases(state.adj1 ,conj,'a')
state.units1,conj = checkAliases(state.units1,conj,'p')
state.era1 ,conj = checkAliases(state.era1 ,conj,'e')
state.special,conj = checkAliases('',conj,'c')
state.adj2 = checkAliases(state.adj2 ,'','a')
state.units2 = checkAliases(state.units2,'','p')
state.era2 = checkAliases(state.era2 ,'','e')
state.conj = conj
state.lang = lang
if p.Error~=nil then
return nil
end
-- calculate date precision value
date1, state.units1, state.precision1 = datePrecision(date1, state.units1)
date2, state.units2, state.precision2 = datePrecision(date2, state.units2)
-- Handle special cases
-- Some complex phrases can be created out of simpler ones. Therefore on pass # 1 we try to create
-- the phrase using complex phrase and if that is not found than on the second pass we try to build
-- the phrase out of the simpler ones
if passNr==1 then
if state.adj1=='circa' and state.nDates == 2 then
state.conj = 'circa2'
state.adj1 = ''
state.adj2 = ''
end
if state.nDates == 2 and state.adj1=='late' and state.adj2=='early' and state.conj=='and'
and state.units1==state.units2 and state.era1==state.era2 then
if state.units1=='century' then
state.conj='turn of the century'
elseif state.units1=='decade' then
state.conj='turn of the decade'
elseif state.units1=='' then
state.conj='turn of the year'
end
state.adj1 = ''
state.adj2 = ''
state.units1 = ''
state.units2 = ''
end
end
state.adj, state.era, state.units, state.precision = state.adj1, state.era1, state.units1, state.precision1
return date1, date2, state
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
function p.Era(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local lang = args['lang']
local dateStr = args['date'] or ''
local eraType = string.lower(args['era'] or '')
dateStr = ISOdate(dateStr, lang, '', '', 1)
if eraType then
eraType = checkAliases(eraType ,'','e')
dateStr = translatePhrase(dateStr, '', eraType, lang, {})
end
return dateStr
end
-- =======================================================================
function p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
local Output=''
local state
-- process inputs and save date in state array
date1, date2, state = processInputParams(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, passNr)
if p.Error~=nil then
return nil
end
local errorStr = string.format(
'\n*conj=%s, adj1=%s, era1=%s, unit1=%s, prec1=%i, adj2=%s, era2=%s, unit2=%s, prec2=%i, special=%s',
state.conj, state.adj1, state.era1, state.units1, state.precision1,
state.adj2, state.era2, state.units2, state.precision2, state.special)
-- call specialized functions
local QScode = ''
if state.special~='' then
Output = otherPhrases(date1, date2, state.special, state.era1, lang, state)
elseif state.conj~='' then
QScode = twoDateQScode(date1, date2, state)
Output = twoDatePhrase(date1, date2, state, lang)
elseif state.adj1~='' or state.era1~='' or state.units1~='' then
Output = oneDatePhrase(date1, state.adj1, state.era1, state.units1, lang, 1, nil, state)
QScode = oneDateQScode(date1, state.adj1, state.era1, state.precision1)
elseif date1~='' then
Output = ISOdate(date1, lang, '', 'dtstart', '100-999')
end
if p.Error~=nil then
return errorStr
end
-- if there is any wikicode in the string than execute it
if mw.ustring.find(Output, '{') then
Output = mw.getCurrentFrame():preprocess(Output)
end
if QScode and #QScode>0 then
QScode = ' <div style="display: none;">date QS:P,' .. QScode .. '</div>'
end
return Output .. QScode
end
-- =======================================================================
function p._complex_date_cer(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, certainty, lang)
-- same as p._complex_date but with extra parameter for certainty: probably, possibly, presumably, etc.
local dateStr = p._complex_date(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
certainty = checkAliases(certainty, conj, 'r')
local LUT = {probably='Q56644435', presumably='Q18122778', possibly='Q30230067', circa='Q5727902' }
if certainty and LUT[certainty] then
local state = {}
date1, date2, state = processInputParams(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, lang, 1)
dateStr = translatePhrase(dateStr, '', certainty, lang, state)
dateStr = string.gsub(dateStr, '(%<div style="display: none;"%>date QS:P,[^%<]+)(%</div%>)', '%1,P1480,' .. LUT[certainty] .. '%2' )
end
return dateStr
end
-- =======================================================================
function p.complex_date(frame)
-- process inputs
local dateStr
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
local date1 = args['date1'] or args['2'] or args['date'] or ''
local date2 = args['date2'] or args['3'] or ''
local conj = args['conj'] or args['1'] or ''
local adj1 = args['adj1'] or args['adj'] or ''
local adj2 = args['adj2'] or ''
local units1 = args['precision1'] or args['precision'] or ''
local units2 = args['precision2'] or args['precision'] or ''
local era1 = args['era1'] or args['era'] or ''
local era2 = args['era2'] or args['era'] or ''
local certainty = args['certainty']
local lang = args['lang']
dateStr = p._complex_date_cer(conj, adj1, date1, units1, era1, adj2, date2, units2, era2, certainty, lang)
if p.Error~=nil then
dateStr = p.Error .. '[[Category:Pages using Complex date template with incorrect parameter]]'
end
return dateStr
end
return p
ppv1t08ydt8a9x36u2iltdsomq7mcr5
Module:I18n/complex date
828
65953
803606
652102
2019-02-24T14:21:17Z
en>RexxS
0
re-sync from Commons
803606
Scribunto
text/plain
--[[
__ __ _ _ ___ _ ___ __ _ _ _
| \/ | ___ __| |_ _| | ___ _|_ _/ |( _ ) _ __ / /__ ___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_)| || |/ _ \| '_ \ / / __/ _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_ | || | (_) | | | |/ / (_| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)___|_|\___/|_| |_/_/ \___\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This code was copied from Commons please request changes there.
Maintainers:
*Jarekt
]]
local Ordinal = require('Module:Ordinal')._Ordinal
local Roman = require('Module:Roman')._Numeral
local linguistic = require('Module:Linguistic')
-- ==========================================================================
-- === Local Helper functions ==============================================
-- ==========================================================================
-- form of French "of" based on "precision", which is wikibase compatible date precision format:
-- 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- 1 2 3 4 5 mill cent deca year month day
local frOf = {'du','du','du','du','du', 'du', 'du', 'de', 'de', 'de', 'du'}
local function decYear(state,lang,case)
--Declension of word "year" in several languages
if state.precision == 9 then
yearCase = {
fi={xxx='vuosien', gen='vuoden', ill='vuoteen', ptv='vuotta', ela='vuodesta', ine='vuonna'},
sl={insP='letoma', ins='letom', gen='leta', loc='letu'},
hsb={insP='lětami', ins='lětom', gen='lěta', loc='lěće'}
}
return yearCase[lang][case] .. ' '
else
return ''
end
end
local function decCentury(lang,case)
--Declension of word "century" in several languages
centuryCase = {
pl={nom='wiek', gen='wieku', ins='wiekiem', loc='wieku', default = 'wiek'},
hsb={nom='lětstotk', gen='lětstotka', ins='lětstotkom', loc='lětstotku', default = 'lětstotk'}
}
return centuryCase[lang][case] or centuryCase[lang]['default']
end
local function decMillennium(lang,case)
--Declension of word "century" in several languages
millenniumCase = {
pl={nom='tysiąclecie', gen='tysiąclecia', ins='tysiącleciem', loc='tysiącleciu', default = 'tysiąclecie'},
hsb={nom='lěttysac', gen='lěttysaca', ins='lěttysacom', loc='lěttysacu', default = 'lěttysac'}
}
return millenniumCase[lang][case] or millenniumCase[lang]['default']
end
-- ==========================================================================
-- === Output data structure ===============================================
-- ==========================================================================
local p = {}
--[[
Multiple formats are possible:
1) string with key words "$date1" and "$date2" which will be replaced later by the date strings
2) function which can be used to create output string
* Function INPUTS:
- date1 and date2 string with date strings. "$date1" and "$date2" can also be used which will be replaced latter
- state is a table with full description of the phrase we are trying to construct. It includes state.precision1 (and 2)
which is wikibase compatible date precision format: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
3) table with following fields:
* table[1] is either string or a function
* table.case is a 2 element table with Grammatical cases used by given language to alter
month name or other adjective related to date # 1 and 2
]]
p.Translations = {
-- ==========================================
-- === Two date phrases =====================
-- ==========================================
['from-until'] = {
nDates = 2,
ar='من $date1 إلى $date2',
['be-tarask']={'з $date1 да $date2', case={'gen','gen'}},
bn='$date1 থেকে $date2 পর্যন্ত',
ca='$date1 fins $date2',
cs='$date1 až $date2',
da='fra $date1 til $date2',
de='$date1 bis $date2',
el='από $date1 έως $date2',
en='from $date1 until $date2',
['en-gb']='from $date1 until $date2',
['en-ca']='from $date1 until $date2',
eo='de $date1 al $date2',
es='de $date1 a $date2',
et='$date1 kuni $date2',
fa='از $date1 تا $date2',
fi='$date1–$date2', -- välisenä aikana
fr=function(date1, date2, state) -- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision > 10 then -- precision > month
return 'du $date1 au $date2'
else -- "DE septebmbre" / "D'octobre"
return linguistic.of(date1, 'fr') .. " à $date2"
end
end,
gl='de $date1 a $date2', --Template:From until used 'desde $date1 ata $date2'
he='מ- $date1 עד $date2',
hr='od $date1 do $date2',
hsb={'wot $date1 do $date2', case={'gen','gen'}},
hu='$date1 – $date2',
it='dal $date1 al $date2',
ja='$date1から$date2まで',
mk='од $date1 до $date2',
ml='$date1 മുതൽ $date2 വരെ',
nb='fra $date1 til $date2',
nds='$date1 bet $date2',
nn='fra $date1 til $date2',
nl='$date1 tot $date2',
no='fra $date1 til $date2',
pl={'od $date1 do $date2', case={'gen','gen'}},
ro='din $date1 până în $date2',
pt='de $date1 a $date2',
qu="$date1 p'unchawmanta $date2 p\'unchawkama",
ru={'с $date1 по $date2', case={'gen','gen'}},
sk={'od $date1 do $date2', case={'gen','gen'}},
sl={'od $date1 do $date2', case={'gen','gen'}},
sv='från $date1 till $date2',
uk={'з $date1 до $date2', case={'gen','gen'}},
ur='از $date1 تا $date2',
vi='từ $date1 đến $date2',
['zh-hans']='从$date1到$date2',
['zh-hant']='從$date1到$date2',
default='<span style="color:red">$date1 - $date2</span>',
},
['between']={
nDates = 2,
ar='بين $date1 و $date2',
['be-tarask']={'між $date1 і $date2', case={'ins','ins'}},
bn='$date1 এবং $date2-এর মাঝামাঝি',
ca='entre $date1 i $date2',
cs={'mezi $date1 a $date2', case={'ins','ins'}},
da='mellem $date1 og $date2',
de='zwischen $date1 und $date2',
el='μεταξύ $date1 και $date2',
en='between $date1 and $date2',
['en-gb']='between $date1 and $date2',
['en-ca']='between $date1 and $date2',
eo='inter $date1 kaj $date2',
es='entre $date1 y $date2',
et='$date1 ja $date2 vahel',
fa='بین $date1 و $date2',
fi={function(date1, date2, state) return decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='entre $date1 et $date2',
gl='entre $date1 e $date2',
he='בין $date1 ו- $date2',
hr='između $date1 i $date2',
hsb={'mjez $date1 a $date2', case={'ins','ins'}},
hu='$date1 és $date2 között',
hi='$date1 और $date2 के बीच',
it='tra il $date1 e il $date2',
ja='$date1と$date2の間',
mk='помеѓу $date1 и $date2',
ml='$date1, $date2 എന്നിവയ്ക്കിടയ്ക്ക്',
nb='mellom $date1 og $date2',
nds='twischen $date1 un $date2',
nn='mellom $date1 og $date2',
nl='tussen $date1 en $date2',
no='mellom $date1 og $date2',
pl={'między $date1 a $date2', case={'ins','ins'}},
ro='între $date1 și $date2',
pt='entre $date1 e $date2',
qu="$date1 p'unchawpa $date2 p'unchawpapas chawpin",
ru={'между $date1 и $date2', case={'ins','ins'}},
sk={'medzi $date1 a $date2', case={'ins','ins'}},
sl={function(date1, date2, state) return 'med ' .. decYear(state,'sl','insP') .. '$date1 in $date2' end, case={'ins','ins'}},
sv='mellan $date1 och $date2',
uk={'між $date1 та $date2', case={'ins','ins'}},
ur='بین $date1 و $date2',
vec='tra el $date1 e el $date2',
vi='giữa $date1 và $date2',
['zh-hans']='在$date1到$date2之间',
['zh-hant']='在$date1到$date2之間',
default='<span style="color:red">$date1 - $date2</span>',
},
['or'] = {
nDates = 2,
ar='$date1 أو $date2',
['be-tarask']='$date1 ці $date2',
bn='$date1 অথবা $date2',
ca='$date1 o $date2',
cs='$date1 nebo $date2',
da='$date1 eller $date2',
de='$date1 oder $date2',
el='$date1 ή $date2',
en='$date1 or $date2',
['en-gb']='$date1 or $date2',
['en-ca']='$date1 or $date2',
eo='$date1 aŭ $date2',
es='$date1 o $date2',
et='$date1 või $date2',
fa='$date1 یا $date2',
fi='$date1 tai $date2',
fr='$date1 ou $date2',
gl='$date1 ou $date2',
he='$date1 או $date2',
hr='$date1 ili $date2',
hsb='$date1 abo $date2',
hu='$date1 vagy $date2',
it='$date1 o $date2',
ja='$date1または$date2',
mk='$date1 или $date2',
ml='$date1 അഥവാ $date2',
nb='$date1 eller $date2',
nn='$date1 eller $date2',
nds='$date1 oder $date2',
nl='$date1 of $date2',
no='$date1 eller $date2',
pl='$date1 lub $date2',
pt='$date1 ou $date2',
qu='$date1 icha $date2',
ro='$date1 sau $date2',
ru='$date1 или $date2',
sk='$date1 alebo $date2',
sl='$date1 ali $date2',
sv='$date1 eller $date2',
uk='$date1 або $date2',
ur='$date1 یا $date2',
th='$date1 หรือ $date2',
vi='$date1 hoặc $date2',
['zh-hans']='$date1或$date2',
['zh-hant']='$date1或$date2',
default=function(date1, date2, state) return linguistic.conj({date1, date2}, state.lang, 'or') end,
},
['and'] = {
nDates = 2,
ar='$date1 و $date2',
['be-tarask']='$date1 і $date2',
bn='$date1 এবং $date2',
ca='$date1 i $date2',
cs='$date1 a $date2',
da='$date1 og $date2',
de='$date1 und $date2',
el='$date1 και $date2',
en='$date1 and $date2',
['en-gb']='$date1 and $date2',
['en-ca']='$date1 and $date2',
eo='$date1 kaj $date2',
es='$date1 y $date2',
et='$date1 ja $date2',
fa='$date1 و $date2',
fi='$date1 ja $date2',
fr='$date1 et $date2',
gl='$date1 e $date2',
he='$date1 ו- $date2',
hu='$date1 és $date2',
hsb='$date1 a $date2',
it='$date1 e $date2',
ja='$date1および$date2',
mk='$date1 и $date2',
ml='$date1 ഒപ്പം $date2',
nb='$date1 og $date2',
nn='$date1 og $date2',
nds='$date1 un $date2',
nl='$date1 en $date2',
no='$date1 og $date2',
pl='$date1 i $date2',
pt='$date1 e $date2',
qu='$date1wan $date2pas',
ro='$date1 și $date2',
ru='$date1 и $date2',
sk='$date1 a $date2',
sl='$date1 in $date2',
sv='$date1 och $date2',
uk='$date1 та $date2',
ur='$date1 و $date2',
vi='$date1 và $date2',
['zh-hans']='$date1和$date2',
['zh-hant']='$date1和$date2',
default=function(date1, date2, state) return linguistic.conj({date1, date2}, state.lang, 'and') end,
},
-- ==========================================
-- === Prepositions =========================
-- ==========================================
['by'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'да $date1', case={'gen',''}},
bn='$date1 কর্তৃক',
ca='vers $date1',
cs={'kolem $date1', case={'gen',''}},
da='mod $date1',
de='spätestens $date1',
el='έως $date1',
en='by $date1',
['en-gb']='by $date1',
['en-ca']='by $date1',
eo='ne post $date1',
es='para $date1',
et='hiljemalt $date1',
fa='توسط $date1',
fi={function(date1, state) return decYear(state,'fi','ill') .. '$date1 mennessä' end, case={'ill',''}},
fr='$date1 (au plus tard)',
gl='cara a $date1',
he='עד $date1',
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='entro il $date1',
ja='$date1まで',
mk='кон $date1',
ml='$date1-ൽ',
nb='innen $date1',
nn='innan $date1',
nds='$date1 oder vördem al',
nl='tegen $date1',
no='innen $date1',
pl={'przed $date1', case={'ins',''}},
pt='por $date1',
qu="$date1 p'unchawkama",
ro='de $date1',
ru={'к $date1', case={'dat',''}},
sk={'okolo $date1', case={'gen',''}},
sl={function(date1, state) return 'najpozneje ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='senast $date1',
uk={'до $date1', case={'dat',''}},
ur='توسط $date1',
vi='vào $date1', -- need to insert "đã" somewhere later in the sentence -->
['zh-hans']='$date1前后',
['zh-hant']='$date1前后',
default='<span style="color:red">by $date1</span>',
},
['before'] = {
nDates = 1,
ar='قبل $date1',
['be-tarask']={'перад $date1', case={'ins',''}},
bn='$date1-এর আগে',
bg='Преди $date1',
ca='abans $date1',
cs={'před $date1', case={'ins',''}},
da='før $date1',
de='vor $date1',
el='πριν από $date1',
en='before $date1',
['en-gb']='before $date1',
['en-ca']='before $date1',
eo='antaŭ $date1',
es='antes de $date1',
et='enne $date1',
fa='پیش از $date1',
fi={function(date1, state) return 'ennen ' .. decYear(state,'fi','ptv') .. '$date1' end, case={'ptv',''}},
fr='avant $date1',
gl='antes do $date1',
he='לפני $date1',
hr='prije $date1',
hsb={'do $date1', case={'gen',''}},
hu='$date1 előtt',
it='prima del $date1',
ja='$date1より前',
mk='пред $date1',
ml='$date1-നു മുമ്പ്',
nb='før $date1',
nn='før $date1',
nds='vör $date1',
nl='voor $date1',
no='før $date1',
pl={'przed $date1', case={'ins',''}},
pt='antes de $date1',
qu="$date1 p'unchaw ñawpan",
ro='înainte de $date1',
ru={'ранее $date1', case={'gen',''}},
sk={'pred $date1', case={'ins',''}},
sl={function(date1, state) return 'pred ' .. decYear(state,'sl','ins') .. ' $date1' end, case={'ins',''}},
sv='före $date1',
uk={'до $date1', case={'gen',''}},
ur='قبل $date1',
vi='trước $date1',
['zh-hans']='在$date1之前',
['zh-hant']='在$date1之前',
default='<span style="color:red">before $date1</span>',
},
['from'] = {
nDates = 1,
ar='من $date1',
['be-tarask']={'з $date1', case={'gen',''}},
bn='$date1 থেকে',
cs={'od $date1', case={'gen',''}},
de='Seit $date1',
el='από $date1',
en='from $date1',
['en-gb']='from $date1',
['en-ca']='from $date1',
eo='ekde $date1',
es='desde $date1',
et='alates $date1',
fa='از $date1',
fi={function(date1, state) return decYear(state,'fi','ela') .. '$date1 alkaen' end, case={'ela',''}},
fr= function(date1, state) return 'à partir '.. frOf[state.precision] ..' $date1' end,
gl='desde o $date1',
he='מ- $date1',
hr='od $date1',
hsb={'wot $date1', case={'gen',''}},
it='dal $date1',
ja='$date1から',
nds='vonaf $date1',
mk='од $date1',
nb='fra $date1',
nn='frå $date1',
ml='$date1 മുതൽ',
nl='vanaf $date1',
no='fra $date1',
pl={'od $date1', case={'gen',''}},
qu="$date1 p'unchawmanta",
ro='din $date1',
ru={'С $date1', case={'gen',''}},
sk={'od $date1', case={'gen',''}},
sl={function(date1, state) return 'od ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='från $date1',
uk={'з $date1', case={'gen',''}},
ur='از $date1',
vi='từ $date1',
['zh-hans']='自$date1',
['zh-hant']='自$date1',
default='<span style="color:red">from $date1</span>',
},
['until'] = {
nDates = 1,
['be-tarask']={'усутыч да $date1', case={'gen',''}},
bn='$date1 পর্যন্ত',
cs={'do $date1', case={'gen', ''}},
de='bis $date1',
en='until $date1',
['en-gb']='until $date1',
['en-ca']='until $date1',
eo='ĝis $date1',
es='hasta $date1',
fr=function(date1, state)
if state.precision > 10 then -- precision > month
return "jusqu'au $date1"
else
return "jusqu'à $date1"
end
end,
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='al $date1',
nb='til $date1',
nl='tot $date1',
nn='til $date1',
ml='$date1 വരെ',
pl={'do $date1', case={'gen',''}},
ru={'вплоть до $date1', case={'gen',''}},
sk={'do $date1', case={'gen', ''}},
uk={'аж до $date1', case={'gen',''}},
ur='تک $date1',
['zh-hans']='直到$date1',
['zh-hant']='直到$date1',
default='<span style="color:red">until $date1</span>',
},
['after'] = {
nDates = 1,
ar='بعد $date1',
['be-tarask']={'пасьля $date1', case={'gen',''}},
bn='$date1-এর পরে',
ca='després de $date1',
cs={'po $date1', case={'loc',''}},
de='nach $date1',
da='efter $date1',
el='μετά από $date1',
en='after $date1',
['en-gb']='after $date1',
['en-ca']='after $date1',
eo='post $date1',
es='después de $date1',
et='pärast $date1',
fa='پس از $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 jälkeen' end, case={'gen',''}},
fr='après $date1',
gl='despois do $date1',
he='לאחר $date1',
hr='nakon $date1',
hu='$date1 után',
hsb={'po $date1', case={'loc',''}},
it='dopo il $date1',
ja='$date1より後',
mk='по $date1',
ml='$date1-നു ശേഷം',
nb='etter $date1',
nn='etter $date1',
nds='na $date1',
nl='na $date1',
no='etter $date1',
pl={'po $date1', case={'loc',''}},
pt='após $date1',
qu="$date1 p'unchaw qhipan",
ro='după $date1',
ru={'после $date1', case={'gen',''}},
sk={'po $date1', case={'loc',''}},
sl={function(date1, state) return 'po ' .. decYear(state,'sl','loc') .. ' $date1' end, case={'loc',''}},
sv='efter $date1',
uk={'після $date1', case={'gen',''}},
ur='بعد $date1',
vi='sau $date1',
['zh-hans']='在$date1之后',
['zh-hant']='在$date1之後',
default='<span style="color:red">after $date1</span>',
},
-- ==========================================
-- === Adjectives ===========================
-- ==========================================
['early'] = {
nDates = 1,
ar='بداية $date1',
['be-tarask']={'пачатак $date1', case={'gen',''}},
bn='$date1-এর শুরুতে',
ca='al principi de $date1',
cs={'začátek $date1', case={'gen',''}},
da='tidlig $date1',
de='Anfang $date1',
el='αρχές του $date1',
en='early $date1',
['en-gb']='early $date1',
['en-ca']='early $date1',
eo='komence de $date1',
es='a principios de $date1',
et='$date1 algus',
fa='اوایل $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alku' end, case={'gen',''}},
fr='début $date1',
gl='a principios de $date1',
he='תחילת $date1',
hr='početak $date1',
hsb={'spočatk $date1', case={'gen',''}},
hu='$date1 elején',
it='inizio del $date1',
ja='$date1初頭',
mk='почеток на $date1',
ml='$date1 ആദ്യപാദം',
nb='tidlig $date1',
nn='tidleg $date1',
nds='Anfang $date1',
nl='begin $date1',
no='tidlig $date1',
pl={'początek $date1', case={'gen',''}},
pt='a princípios de $date1',
qu="$date1 ñawpaq p'unchawkunapi",
ro='începând cu $date1',
ru={'начало $date1', case={'gen',''}},
sk={'začiatok $date1', case={'gen',''}},
sl=function(date1, state)-- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision == 9 then -- precision > month
return 'zgodnje leto $date1'
else -- "DE septebmbre" / "D'octobre"
return 'zgodnji $date1'
end
end,
sv='början av $date1',
uk={'початок $date1', case={'gen',''}},
ur='ابتدا $date1',
vi='đầu năm $date1',
['zh-hans']='$date1早期',
['zh-hant']='$date1早期',
default='<span style="color:red">early $date1</span>',
},
['mid'] = {
nDates = 1,
ar='منتصف $date1',
['be-tarask']={'сярэдзіна $date1', case={'gen',''}},
bn='$date1-এর মাঝামাঝি',
ca='a mitjan $date1',
cs={'polovina $date1', case={'gen',''}},
da='midten af $date1',
de='Mitte $date1',
el='μέσα του $date1',
en='mid $date1',
['en-gb']='mid $date1',
['en-ca']='mid $date1',
eo='meze de $date1',
es='a mediados de $date1',
et='$date1 keskel',
fa='اواسط $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 puoliväli' end, case={'gen',''}},
fr='mi $date1',
gl='a mediados de $date1',
he='אמצע $date1',
hr='sredina $date1',
hsb={'srjedź $date1', case={'gen',''}},
hu='$date1 közepén',
it='metà del $date1',
ja='$date1半ば',
mk='средина на $date1',
ml='$date1 മദ്ധ്യം',
nb='midten av $date1',
nn='midten av $date1',
nds='Midd $date1',
nl='midden $date1',
no='midten av $date1',
pl={'środek $date1', case={'gen',''}},
pt='meados de $date1',
qu="$date1 chawpin p'unchawkunapi",
ro='mijlocul lui $date1',
ru={'середина $date1', case={'gen',''}},
sk={'polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'sredina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='mitten av $date1',
uk={'середина $date1', case={'gen',''}},
ur='وسط $date1',
vi='giữa năm $date1',
['zh-hans']='$date1中期',
['zh-hant']='$date1中期',
default='<span style="color:red">mid $date1</span>',
},
['late'] = {
nDates = 1,
ar='نهاية $date1',
bn='$date1-এর শেষে',
['be-tarask']={'канец $date1', case={'gen',''}},
ca='al final de $date1',
cs='konec $date1',
da='sen $date1',
de='Ende $date1',
el='τέλη του $date1',
en='late $date1',
['en-gb']='late $date1',
['en-ca']='late $date1',
eo='fine de $date1',
es='a fines de $date1',
et='$date1 lõpp',
fa='اواخر $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppu' end, case={'gen',''}},
fr='fin $date1',
gl='a finais de $date1',
he='סוף $date1',
hr='kraj $date1',
hsb={'kónc $date1', case={'gen',''}},
hu='$date1 végén',
it='fine del $date1',
ja='$date1末',
mk='крајот на $date1',
ml='$date1 അന്ത്യപാദം',
nb='sent $date1',
no='seint $date1',
nds='Enn $date1',
nl='eind $date1',
no='sent $date1',
pl={'koniec $date1', case={'gen',''}},
pt='ao final de $date1',
qu="$date1 qhipaq p'unchawkunapi",
ro='sfârșitul lui $date1',
ru={'конец $date1', case={'gen',''}},
sk={'koniec $date1', case={'gen',''}},
sl={function(date1, state) return 'pozno ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='slutet av $date1',
uk={'кінець $date1', case={'gen',''}},
ur='اخیر $date1',
vi='cuối năm $date1',
['zh-hans']='$date1晚期',
['zh-hant']='$date1晚期',
default='<span style="color:red">late $date1</span>',
},
['circa'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'каля $date1', case={'gen',''}},
bn='প্রায় $date1',
ca='cap a $date1',
cs='cca $date1',
da='ca. $date1',
de='etwa $date1',
el='περ. $date1',
en='circa $date1',
['en-gb']='circa $date1',
['en-ca']='circa $date1',
eo='ĉirkaŭ $date1',
es='hacia $date1', -- suggestion use "hacia"/"hacia el" or leave the latin circa or ca.
et='ca. $date1',
fa='حوالی $date1', -- template:Circa used 'حدود $date1'
fi={function(date1, state) return 'suunnilleen ' .. decYear(state,'fi','ine') .. '$date1' end, case={'ine',''}}, -- template:Circa used 'noin $date1'
fr=function(date1, state)
if state.precision >= 11 then -- precision >= day
return 'vers le $date1'
else
return 'vers $date1'
end
end,
gl='entre $date1', -- template:Circa used 'cara a $date1'
he='בסביבות $date1',
hr='oko $date1',
hsb={'wokoło $date1', case={'gen',''}},
hu='$date1 körül',
it='$date1 circa',
ja='$date1頃',
ko='$date1경',
mk='~ $date1', -- template:Circa used 'o. $date1'
ml= 'ഉദ്ദേശം $date1',
nb='ca. $date1',
nn='ca. $date1',
nds='üm un bi $date1',
nl='ca. $date1',
no='ca. $date1',
pl={'około $date1', case={'gen',''}},
pt='cerca de $date1',
qu='$date1chá',
ro='circa $date1',
ru={'около $date1', case={'gen',''}},
sk='cca $date1',
sl={function(date1, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='c:a $date1', -- template:Circa used 'ca $date1'
uk={'близько $date1', case={'gen', ''}},
ur='قریباً $date1',
vi='vào khoảng $date1',
['zh-hans']='约$date1',
['zh-hant']='約$date1',
default='<span style="color:red">circa $date1</span>',
},
['uncertain'] = {
nDates = 1,
['zh-hans']='$date1(?)',
['zh-hant']='$date1(?)',
default='$date1 (?)',
},
probably = { -- see Q56644435
nDates = 1,
ast = 'probablemente $date1',
bn = 'সম্ভবত $date1',
ca = 'probablement $date1',
cs = 'pravděpodobně $date1',
de = 'wahrscheinlich $date1',
el = 'πιθανόν $date1',
en = 'probably $date1',
['en-gb']='probably $date1',
['en-ca']='probably $date1',
eo = 'probable $date1',
es = 'probable $date1',
fa = 'احتمالاً $date1',
fr = 'probablement $date1',
he = 'כפי הנראה $date1',
hu = 'feltehetőleg $date1',
it = 'probabile $date1',
mk = 'веројатно $date1',
ml = 'ഒരുപക്ഷേ $date1',
nl = 'waarschijnlijk $date1',
pl = 'prawdopodobnie $date1',
pt = 'provavelmente $date1',
ru = 'вероятно $date1',
sv = 'antagligen $date1',
uk = 'ймовірно $date1',
default='<span style="color:red">probably $date1</span>',
},
possibly = { -- see Q30230067
nDates = 1,
ast = 'posiblemente $date1',
bn = 'যথাসম্ভব $date1',
cs = 'možná $date1',
de = 'möglicherweise $date1',
en = 'possibly $date1',
['en-gb']='possibly $date1',
['en-ca']='possibly $date1',
eo = 'eble $date1',
el = 'πιθανόν $date1',
fa = 'احتمالاً $date1',
fr = 'peut-être $date1',
hu = 'talán $date1',
mk = 'можеби $date1',
ml = 'മിക്കവാറും $date1',
nl = 'mogelijk $date1',
pl = 'być może $date1',
pt = 'possivelmente $date1',
ro = 'poate $date1',
ru = 'возможно $date1',
sv = 'möjligen $date1',
uk = 'можливо $date1',
default='<span style="color:red">possibly $date1</span>',
},
presumably = { -- see Q18122778
nDates = 1,
ast = 'presumiblemente $date1',
de = 'vermutlich $date1',
en = 'presumably $date1',
['en-gb']='presumably $date1',
['en-ca']='presumably $date1',
fr = 'présumé $date1',
it = 'presumibilmente $date1',
ml = 'ഊഹപ്രകാരം $date1',
ru = 'предположительно $date1',
pl = 'przypuszczalnie $date1',
uk = 'здогадно $date1',
default='<span style="color:red">presumably $date1</span>',
},
['year unknown']= {
nDates = 0,
ca='Any desconegut',
['be-tarask']='Год невядомы',
bn='অজানা বছর',
cs='neznámý rok',
de='Jahr unbekannt',
el='άγνωστο έτος',
en='year unknown',
['en-gb']='year unknown',
['en-ca']='year unknown',
eo='jaro nesciata',
es='Año desconocido',
fa='سال نامعلوم',
fi='tuntematon vuosi',
fr='Année inconnue',
he='שנה לא ידועה',
hsb='lěto njeznate',
hr='nepoznata godina',
it='anno sconosciuto',
ja='年代不明',
la='Sine anno',
ml='വർഷം ലഭ്യമല്ല',
mk='непозната година',
nb='ukjent år',
nn='ukjent år',
nl='zonder jaar',
no='ukjent år',
pl='rok nieznany',
qu='Mana riqsisqa wata',
ru='Год неизвестен',
sk='neznámy rok',
sl='neznano leto',
sv='Okänt år',
uk='рік невідомий',
ur='سال نامعلوم',
vi='không rõ năm',
['zh-hans']='年份未知',
['zh-hant']='年份未知',
default='<span style="color:red">year unknown</span>',
},
-- ==========================================
-- === Parts of a whole =====================
-- ==========================================
['firsthalf'] = {
nDates = 1,
ar='النصف الأول من $date1',
['be-tarask']={'першая палова $date1', case={'gen',''}},
bn='$date1-এর প্রথমার্ধ',
ca='1a meitat de $date1',
cs={'1. polovina $date1', case={'gen',''}},
da='1. halvdel af $date1',
de='1. Hälfte $date1',
el='πρώτο μισό του $date1',
en='first half of $date1',
['en-gb']='first half of $date1',
['en-ca']='first half of $date1',
eo='unua duono de $date1',
es='primera mitad de $date1',
et='$date1 esimene pool',
fa='نیمهٔ نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alkupuolisko' end, case={'gen',''}},
fr='1<sup>re</sup> moitié de $date1',
gl='primeira metade de $date1',
he='במחצית הראשונה של $date1',
hsb={'prěnja połojca $date1', case={'gen',''}},
hu='$date1 első felében',
hr='prva polovina $date1',
it='prima metà del $date1',
ja='$date1上半期',
mk='прва половина на $date1',
ml='$date1 ആദ്യപകുതി',
nb='første halvdel av $date1',
nn='fyrste halvdelen av $date1',
nds='1. Helft $date1',
nl='eerste helft $date1',
no='første halvdel av $date1',
pl={'pierwsza połowa $date1', case={'gen',''}},
pt='primeira metade de $date1',
qu='$date1 ñawpaq kuskanpi',
ro='prima jumătate a lui $date1',
ru={'первая половина $date1', case={'gen',''}},
sk={'1. polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'prva polovica ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första halvan av $date1',
uk={'перша половина $date1', case={'gen',''}},
ur='نصف اول $date1',
vi='nửa đầu năm $date1',
['zh-hans']='$date1上半期',
['zh-hant']='$date1上半期',
default='<span style="color:red">first half of $date1</span>',
},
['secondhalf'] = {
nDates = 1,
ar='النصف الثاني من $date1',
['be-tarask']={'другая палова $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয়ার্ধ',
ca='2a meitat de $date1',
cs={'2. polovina $date1', case={'gen',''}},
da='2. halvdel af $date1',
de='2. Hälfte $date1',
el='δεύτερο μισό του $date1',
en='second half of $date1',
['en-gb']='second half of $date1',
['en-ca']='second half of $date1',
eo='dua duono de $date1',
es='segunda mitad de $date1',
et='$date1 teine pool',
fa='نیمهٔ دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppupuolisko' end, case={'gen',''}},
fr='2<sup>e</sup> moitié de $date1',
gl='segunda metade de $date1',
he='במחצית השנייה של $date1',
hsb={'druha połojca $date1', case={'gen',''}},
hr='druga polovina $date1',
hu='$date1 második felében',
it='seconda metà del $date1',
ja='$date1下半期',
mk='втора половина на $date1',
ml='$date1 രണ്ടാം പകുതി',
nb='andre halvdel av $date1',
nn='andre halvdelen av $date1',
nds='2. Helft vun $date1',
nl='tweede helft $date1',
no='andre halvdel av $date1',
pl={'druga połowa $date1', case={'gen',''}},
pt='segunda metade de $date1',
qu='$date1 qhipaq kuskanpi',
ro='a doua jumătate a lui $date1',
ru={'вторая половина $date1', case={'gen',''}},
sk={'2. polovica $date1', case={'gen',''}},
sl={'druga polovica {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sv='andra halvan av $date1',
uk={'друга половина $date1', case={'gen',''}},
ur='نصف آخر $date1',
vi='nửa cuối năm $date1',
['zh-hans']='$date1下半期',
['zh-hant']='$date1下半期',
default='<span style="color:red">second half of $date1</span>',
},
['1quarter']= {
nDates = 1,
bn='$date1-এর প্রথম ত্রৈমাসিক',
['be-tarask']={'першая чвэрць $date1', case={'gen',''}},
cs={'1. čtvrtina $date1', case={'gen',''}},
de='erstes Viertel $date1',
en='first quarter of $date1',
['en-gb']='first quarter of $date1',
['en-ca']='first quarter of $date1',
eo='unua kvarono de $date1',
es='primer cuarto del $date1',
fa='ربع نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 ensimmäinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'premier quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון ראשון של $date1',
hsb={'prěnja štwórćina $date1', case={'gen',''}},
hr='prva četvrtina $date1',
it='primo quarto del $date1',
ja='$date1第1四半期',
ml='$date1 ഒന്നാം ത്രൈമാസം',
nb='første kvartal av $date1',
nl='eerste kwart $date1',
nn='fyrste kvartalet av $date1',
no='første kvartal av $date1',
pl={'pierwsza ćwiartka $date1', case={'gen',''}},
qu='$date1 ñawpaq ñiqin tawa phatmanpi',
ru={'первая четверть $date1', case={'gen',''}},
sk={'1. štvrtina $date1', case={'gen',''}},
sl={'prva četrtina {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sl={function(date1, state) return 'prva četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första kvartalet av $date1',
uk={'1-ша чверть $date1', case={'gen',''}},
ur='پہلی سہ ماہی $date1',
vi='quý 1 năm $date1',
['zh-hans']='$date1第一季度',
['zh-hant']='$date1第一季度',
default='<span style="color:red">first quarter of $date1</span>',
},
['2quarter']= {
nDates = 1,
['be-tarask']={'другая чвэрць $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয় ত্রৈমাসিক',
cs={'2. čtvrtina $date1', case={'gen',''}},
de='zweites Viertel $date1',
en='second quarter of $date1',
['en-gb']='second quarter of $date1',
['en-ca']='second quarter of $date1',
eo='dua kvarono de $date1',
es='segundo cuarto del $date1',
fa='ربع دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 toinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'deuxième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שני של $date1',
hsb={'druha štwórćina $date1', case={'gen',''}},
hr='druga četvrtina $date1',
it='secondo quarto del $date1',
ja='$date1第2四半期',
ml='$date1 രണ്ടാം ത്രൈമാസം',
nb='andre kvartal av $date1',
nl='tweede kwart $date1',
nn='andre kvartalet av $date1',
no='andre kvartal av $date1',
pl={'druga ćwiartka $date1', case={'gen',''}},
qu='$date1 iskay ñiqin tawa phatmanpi',
ru={'вторая четверть $date1', case={'gen',''}},
sk={'2. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'druga četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='andra kvartalet av $date1',
uk={'2-га чверть $date1', case={'gen',''}},
ur='دوسری سہ ماہی $date1',
vi='quý 2 năm $date1',
['zh-hans']='$date1第二季度',
['zh-hant']='$date1第二季度',
default='<span style="color:red">second quarter of $date1</span>',
},
['3quarter']= {
nDates = 1,
['be-tarask']={'трэцяя чвэрць $date1', case={'gen',''}},
bn='$date1-এর তৃতীয় ত্রৈমাসিক',
cs={'3. čtvrtina $date1', case={'gen',''}},
de='drittes Viertel $date1',
en='third quarter of $date1',
['en-gb']='third quarter of $date1',
['en-ca']='third quarter of $date1',
eo='tria kvarono de $date1',
es='tercer cuarto del $date1',
fa='ربع سوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 kolmas neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'troisième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שלישי של $date1',
hsb={'třeća štwórćina $date1', case={'gen',''}},
hr='treća četvrtina $date1',
it='terzo quarto del $date1',
ja='$date1第3四半期',
ml='$date1 മൂന്നാം ത്രൈമാസം',
nb='tredje kvartal av $date1',
nl='derde kwart $date1',
nn='tredje kvartalet av $date1',
no='tredje kvartal av $date1',
pl={'trzecia ćwiartka $date1', case={'gen',''}},
qu='$date1 kimsa ñiqin tawa phatmanpi',
sk={'3. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'tretja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
ru={'третья четверть $date1', case={'gen',''}},
sv='tredje kvartalet av $date1',
uk={'3-тя чверть $date1', case={'gen',''}},
ur='تیسری سہ ماہی $date1',
vi='quý 3 năm $date1',
['zh-hans']='$date1第三季度',
['zh-hant']='$date1第三季度',
default='<span style="color:red">third quarter of $date1</span>',
},
['4quarter']= {
nDates = 1,
['be-tarask']={'чацьвёртая чвэрць $date1', case={'gen',''}},
bn='$date1-এর চতুর্থ ত্রৈমাসিক',
cs={'4. čtvrtina $date1', case={'gen',''}},
de='letztes Viertel $date1',
en='fourth quarter of $date1',
['en-gb']='fourth quarter of $date1',
['en-ca']='fourth quarter of $date1',
eo='kvara kvarono de $date1',
es='último cuarto del $date1',
fa='ربع چهارم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 viimeinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'quatrième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון רביעי של $date1',
hsb={'poslednja štwórćina $date1', case={'gen',''}},
hr='četvrta četvrtina $date1',
it='quarto quarto del $date1',
ja='$date1第4四半期',
ml='$date1 നാലാം ത്രൈമാസം',
nb='fjerde kvartal av $date1',
nl='vierde kwart $date1',
nn='tredje kvartalet av $date1',
no='fjerde kvartal av $date1',
pl={'czwarta ćwiartka $date1', case={'gen',''}},
qu='$date1 qhipaq ñiqin tawa phatmanpi',
ru={'последняя четверть $date1', case={'gen',''}},
sk={'4. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'zadnja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='fjärde kvartalet av $date1',
uk={'4-та чверть $date1', case={'gen',''}},
ur='چوتھی سہ ماہی $date1',
vi='quý 4 năm $date1',
['zh-hans']='$date1第四季度',
['zh-hant']='$date1第四季度',
default='<span style="color:red">forth quarter of $date1</span>',
},
-- ==========================================
-- === Seasons ==============================
-- ==========================================
['spring'] = {
nDates = 1,
ar='ربيع $date1',
['be-tarask']='вясна $date1',
bn='বসন্তকাল $date1',
ca='primavera $date1',
cs={'jaro $date1', case={'gen', ''}},
da='Forår $date1',
de='Frühling $date1',
en='Spring $date1',
['en-gb']='Spring $date1',
['en-ca']='Spring $date1',
eo='printempo $date1',
es='primavera de $date1',
et='$date1 kevad',
fa='بهار $date1',
fi='Kevät $date1',
fr='printemps $date1',
gl='primavera $date1',
he='אביב $date1',
hr='proljeće $date1',
hsb='nalěćo $date1',
hu='$date1 tavasza',
it='primavera $date1',
ja='{{#if:$date1|$date1年}}春',
mk='пролет $date1',
ml='$date1 വസന്തം',
nb='våren $date1',
nds='Vörjohr $date1',
nn='våren $date1',
nl='voorjaar $date1',
no='våren $date1',
pl='wiosna $date1',
pt='primavera de $date1',
ro='primăvara lui $date1',
ru='весна $date1',
sk={'jar $date1', case={'gen', ''}},
sl='pomlad $date1',
sv='våren $date1',
uk='весна $date1',
ur='بہار $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}春季',
['zh-hant']='{{#if:$date1|$date1年}}春季',
default='<span style="color:red">Spring $date1</span>',
},
['summer'] = {
nDates = 1,
ar='صيف $date1',
['be-tarask']='лета $date1',
bn='গ্রীষ্মকাল $date1',
ca='estiu $date1',
cs={'léto $date1', case={'gen', ''}},
da='Sommer $date1',
de='Sommer $date1',
en='Summer $date1',
['en-gb']='Summer $date1',
['en-ca']='Summer $date1',
eo='somero $date1',
es='verano de $date1',
et='$date1 suvi',
fa='تابستان $date1',
fi='Kesä $date1',
fr='été $date1',
gl='verán $date1',
he='קיץ $date1',
hr='ljeto $date1',
hsb='lěćo $date1',
hu='$date1 nyara',
it='estate $date1',
ja='{{#if:$date1|$date1年}}夏',
mk='лето $date1',
ml='$date1 വേനൽ',
nb='sommeren $date1',
nds='Sommer $date1',
nn='sumaren $date1',
nl='zomer $date1',
no='sommeren $date1',
pl='lato $date1',
pt='verão de $date1',
ro='vara lui $date1',
ru='лето $date1',
sk={'leto $date1', case={'gen', ''}},
sl='poletje $date1',
sv='sommaren $date1',
uk='літо $date1',
ur='گرما $date1',
vi='mùa hè năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}夏季',
['zh-hant']='{{#if:$date1|$date1年}}夏季',
default='<span style="color:red">Summer $date1</span>',
},
['autumn'] = {
nDates = 1,
ar='خريف $date1',
['be-tarask']='восень $date1',
bn='শরত্কাল $date1',
ca='tardor $date1',
cs={'podzim $date1', case={'gen', ''}},
da='Efterår $date1',
de='Herbst $date1',
en='Autumn $date1',
['en-gb']='Autumn $date1',
['en-ca']='Autumn $date1',
eo='aŭtuno $date1',
es='otoño de $date1',
et='$date1 sügis',
fa='پاییز $date1',
fi='Syksy $date1',
fr='automne $date1',
gl='outono $date1',
he='סתיו $date1',
hr='jesen $date1',
hsb='nazyma $date1',
hu='$date1 ősze',
it='autunno $date1',
ja='{{#if:$date1|$date1年}}秋',
mk='есен $date1',
ml='$date1 ശരത്കാലം',
nb='høsten $date1',
nds='Harvst $date1',
nn='hausten $date1',
nl='herfst $date1',
no='høsten $date1',
pl='jesień $date1',
pt='outono de $date1',
ro='toamna lui $date1',
ru='осень $date1',
sk={'jeseň $date1', case={'gen', ''}},
sl='jesen $date1',
sv='hösten $date1',
uk='осінь $date1',
ur='خزاں $date1',
vi='mùa thu năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}秋季',
['zh-hant']='{{#if:$date1|$date1年}}秋季',
default='<span style="color:red">Autumn $date1</span>',
},
['winter'] = {
nDates = 1,
ar='شتاء $date1',
['be-tarask']='зіма $date1',
bn='শীতকাল $date1',
ca='hivern $date1',
cs={'zima $date1', case={'gen', ''}},
da='Vinter $date1',
de='Winter $date1',
en='Winter $date1',
['en-gb']='Winter $date1',
['en-ca']='Winter $date1',
eo='vintro $date1',
es='invierno de $date1',
et='$date1 talv',
fa='زمستان $date1',
fi='Talvi $date1',
fr='hiver $date1',
gl='inverno $date1',
he='חורף $date1',
hr='zima $date1',
hsb='zyma $date1',
hu='$date1 tele',
it='inverno $date1',
ja='{{#if:$date1|$date1年}}冬',
mk='зима $date1',
ml='$date1 ശൈത്യകാലം',
nds='Winter $date1',
nl='winter $date1',
nb='vinteren $date1',
nn='vinteren $date1',
no='vinteren $date1',
pl='zima $date1',
pt='inverno de $date1',
ro='iarna lui $date1',
ru='зима $date1',
sk={'zima $date1', case={'gen', ''}},
sl='zima $date1',
sv='vintern $date1',
uk='зима $date1',
ur='سرما $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}冬季',
['zh-hant']='{{#if:$date1|$date1年}}冬季',
default='<span style="color:red">Winter $date1</span>',
},
-- ==========================================
-- === Decade/Century/Millennium ============
-- ==========================================
['decade'] = {
nDates = 1,
ar='أعوام $date1',
['be-tarask']='$date1-я гады',
bn='$date1-এর দশক',
ca='dècada del $date1',
cs=function(date1, state) return ((date1 % 100 == 0) and 'první dekáda' or ((date1 % 100) .. '. léta')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. století' end,
da="$date1'erne",
de='$date1er-Jahre',
el='δεκαετία του $date1',
en='$date1s',
['en-gb']='$date1s',
['en-ca']='$date1s',
eo='$date1-aj jaroj',
es='años $date1',
et='$date1ndad',
fa='دههٔ $date1',
fi='$date1-luvulla',
fr='années $date1',
gl='anos $date1',
he='שנות ה-$date1',
hr='$date1-ih',
hsb='$date1te lěta',
hu='$date1-s évek',
it="{{#ifexpr: $date1 mod 100 = 0 | primo decennio del {{Roman|{{#expr: ($date1 / 100) + 1}}}} sec. | anni '{{#expr: $date1 mod 100 }} del {{Roman|{{#expr: floor($date1 / 100) + 1}}}} sec.}}",
ja='$date1年代',
mk='$date1-ти',
ml='ദശകം $date1-ൽ',
nb='$date1-årene',
nn='$date1-åra',
nds='Johren $date1',
nl='jaren $date1',
no='$date1-årene',
pl=function(date1, state)
local str = 'lata '..(date1 % 100)..'. '
if (date1 % 100 == 0) then
str = 'pierwsza dekada '
elseif (date1 % 100 == 10) then
str = 'druga dekada '
end
return str .. Roman(math.floor(date1 / 100) + 1) .. ' wieku'
end,
pt='década de $date1',
qu='$date1 watakuna',
ro='anii $date1',
ru='$date1-е годы',
sk=function(date1, state) return ((date1 % 100 == 0) and 'prvé desaťročie' or ((date1 % 100) .. '. roky')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. storočia' end,
sl='$date1-ta leta',
sv='$date1-talet{{#switch:$date1|1300|1400|1500|1600|1700|1800|1900|2000|2100= (decennium)}}',
uk='$date1-ті роки',
ur='دہائی $date1',
vi='thập niên $date1',
['zh-hans']='$date1年代',
['zh-hant']='$date1年代',
default='<span style="color:red">$date1s</span>',
},
['century'] = {
nDates = 1,
ar='القرن $date1',
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' стагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' শতাব্দী' end,
ca='segle {{small-caps|{{lc:{{Roman year|$date1}}}}}}',
cs='$date1. století',
da='$date1. århundrede',
de='$date1. Jahrhundert',
el='$date1ος αιώνας',
en=function(date1, state) return Ordinal(date1, 'en') .. ' century' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' century' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' century' end,
eo='$date1-a jarcento',
es=function(date1, state) return 'siglo ' .. Roman(date1) end,
et='$date1. sajand',
fa='سدهٔ $date1',
fi='{{#expr: ($date1 - 1) * 100}}-luvulla',
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle' end,
gl=function(date1, state) return 'século ' .. Roman(date1) end,
he='המאה ה-$date1',
hr='$date1. stoljeće',
hsb='$date1. lětstotk',
hu='$date1. évszázad',
it=function(date1, state) return Roman(date1) .. ' sec.' end,
ja='$date1世紀',
la='saeculum $date1',
mk=function(date1, state) return Roman(date1) .. ' век' end,
ml='$date1-ആം നൂറ്റാണ്ട്',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' århundre' end,
nn=function(date1, state) return 'det ' .. Ordinal(date1, 'no') .. ' århundre' end,
nds='$date1. Johrhunnert',
nl='$date1e eeuw',
no=function(date1, state) return Ordinal(date1, 'no') .. ' århundre' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decCentury('pl',state.case) end,
pt=function(date1, state) return 'século ' .. Roman(date1) end,
qu='$date1 ñiqin pachakwata',
ro='secolul al $date1-lea',
ru=function(date1, state) return Roman(date1) .. ' век' end,
sk='$date1. storočie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. stoletje' end,
sv='{{#expr:$date1 - 1}}00-talet',
tt=function(date1, state) return Roman(date1) .. ' гасыр' end,
uk=function(date1, state) return Roman(date1) .. ' ст.' end,
ur='صدی $date1',
vi='thế kỷ $date1',
['zh-hans']='$date1世纪',
['zh-hant']='$date1世紀',
default='<span style="color:red">$date1 century</span>',
},
['millennium'] = {
nDates = 1,
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' тысячагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' সহস্রাব্দ' end,
cs='$date1. tisíciletí',
de='$date1. Jahrtausend',
en=function(date1, state) return Ordinal(date1, 'en') .. ' millennium' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' millennium' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' millennium' end,
eo='$date1-a jarmilo',
es=function(date1, state) return Roman(date1) .. ' milenio' end,
fa=function(date1, state) return 'هزارهٔ ' .. Ordinal(date1, 'fa') end,
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' millénaire' end,
hr='$date1. tisućljeće',
hsb='$date1. lěttysac',
it=function(date1, state) return Roman(date1) .. ' millennio' end,
ja='$date1千年紀',
la='millennium $date1',
ml='$date1-മത്തെ സഹസ്രാബ്ദം',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' årtusen' end,
nn=function(date1, state) return Ordinal(date1, 'no') .. ' tusenår' end,
nl='$date1e millennium',
no=function(date1, state) return Ordinal(date1, 'no') .. ' årtusen' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decMillennium('pl',state.case) end,
pt=function(date1, state) return Roman(date1) .. ' milénio' end,
qu='$date1 ñiqin waranqawata',
ru=function(date1, state) return Roman(date1) .. ' тысячелетие' end,
sk='$date1. tisícročie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. tisočletje' end,
sv=function(date1, state) return Ordinal(date1, 'sv') .. ' årtusendet' end,
uk=function(date1, state) return Roman(date1) .. ' тис.' end,
ur=function(date1, state) return 'ہزارہ ' .. Ordinal(date1, 'ur') end,
vi='thiên niên kỷ $date1',
['zh-hans']=function(date1, state) return Ordinal(date1, 'zh-hans') .. '千年' end,
['zh-hant']=function(date1, state) return Ordinal(date1, 'zh-hant') .. '千年' end,
--default='{{Please translate|$date1 millennium|Module:I18n/complex date}}',
default='<span style="color:red">$date1 millennium</span>',
},
['million'] = {
de = '$date1 Millionen Jahre',
en = '$date1 million years',
['en-gb'] = '$date1 million years',
['en-ca'] = '$date1 million years',
eo = '$date1 milionoj da jaroj',
es = '$date1 millones de años',
nl = '$date1e miljoen jaar',
fr = '$date1 millions d’années',
it = '$date1 milioni di anni',
ml = function(date1, state)
if date1 >= 10 then
return (date1 / 10) .. ' കോടി വർഷങ്ങൾ' -- 100 lakh = 1 crore
else
return (date1 * 10) .. ' ലക്ഷം വർഷങ്ങൾ'
end
end,
nb = '$date1 millioner år',
nl = '$date1 miljoen jaar',
nn = '$date1 millionar år',
pl = '$date1 milionów lat',
pt = '$date1 milhões de anos',
ru = '$date1 миллиона лет',
uk = '$date1 мільйони років',
ur = '$date1 ملین سال',
['zh-hans'] = '$date1百万年',
['zh-hant'] = '$date1百万年',
default='<span style="color:red">$date1 million years</span>',
nDates = 1,
},
['billion'] = {
de = '$date1 Milliarden Jahre',
en = '$date1 billion years',
['en-gb'] = '$date1 billion years',
['en-ca'] = '$date1 billion years',
es = '$date1 mil millones de años',
nl = '$date1e miljard jaar',
fr = '$date1 milliard d’années',
it = '$date1 miliardi di anni',
ml = function(date1, state) return (date1 * 10) .. ' കോടി വർഷങ്ങൾ' end,
nb = '$date1 milliarder år',
nl = '$date1 miljard jaar',
nn = '$date1 milliardar år',
pl = '$date1 miliardów lat',
pt = '$date1 mil milhões de anos',
ru = '$date1 миллиардов лет',
uk = '$date1 мільярдів років',
ur = '$date1 بلین سال',
['zh-hans'] = '$date1十亿年',
['zh-hant'] = '$date1十億年',
default='<span style="color:red$date1 billion years</span>',
nDates = 1,
},
-- ==========================================
-- === Complex phrases ======================
-- ==========================================
-- Phrases which for some languages can be build out of simpler phrases
-- Their "default" state, used when no translation in the requested language was found, returns a "state" like
-- table which is used to call the function again and built the phrase out of simple phrases
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the century'] = {
nDates = 2,
['be-tarask']=function(date1, date2, state) return 'канец ' .. Roman(date1) ..' — пачатак ' .. Roman(date2) ..' стагодзьдзя' end,
bn=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'bn') .. ' শতাব্দীর পালাবদলে' end,
cs='přelom $date1. a $date2. století',
de=function(date1, date2, state) return 'Jahrhundertwende $date1/' .. Ordinal(date2, 'de') end,
en=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en') .. ' century' end,
['en-gb']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-gb') .. ' century' end,
['en-ca']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-ca') .. ' century' end,
eo='ŝanĝo de la $date1-a al la $date2-a jarcentoj',
es=function(date1, date2, state) return 'hacia finales del siglo ' .. Roman(date1) .. ' y principios del siglo ' .. Roman(date2) .. '' end,
fi='{{#expr: $date1 * 100-100}}-luvun loppu ja {{#expr: $date2 * 100-100}}-luvun alku',
fr=function(date1, date2, state) return 'fin du ' .. Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle / début du ' .. Ordinal(date2, 'fr', 'roman', 'm') end,
hr='kraj $date1. i početak $date2. stoljeća',
hsb=function(date1, date2, state) return 'přechod ' .. Roman(date1) ..' do ' .. Roman(date2) ..' lětstotka' end,
it=function(date1, date2, state) return 'fine del ' .. Roman(date1) .. ' sec. e inizio del ' .. Roman(date2) .. ' sec.' end,
ja='$date1世紀から$date2世紀への変わり目',
ml='$date1 നൂറ്റാണ്ട് അവസാനവും $date2 നൂറ്റാണ്ട് ആദ്യവും',
nb=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nb') .. ' århundre/begynnelsen av det ' .. Ordinal(date2, 'nb') .. ' århundre' end,
nl=function(date1, date2, state) return 'eind $date1e/begin $date2e eeuw' end,
nn=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nn') .. ' hundreåret/byrjinga av det ' .. Ordinal(date2, 'nn') .. ' hundreåret' end,
pl=function(date1, date2, state) return 'przełom ' .. Roman(date1) ..' i ' .. Roman(date2) ..' wieku' end,
qu="$date1 ñiqin waranqawatamanta $date2 ñiqin waranqawataman t'ikrasqanpi",
ru=function(date1, date2, state) return 'конец ' .. Roman(date1) ..' — начало ' .. Roman(date2) ..' века' end,
sk='prelom $date1. a $date2. storočia',
sl=function(date1, date2, state) return 'pozno ' .. Ordinal(date1, 'sl') .. '. in zgodnje ' .. Ordinal(date2, 'sl') .. '. stoletje' end,
sv=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'sv') .. ' årtusendets sekelskifte' end,
uk=function(date1, date2, state) return 'кін. ' .. Roman(date1) ..' ст. — поч. ' .. Roman(date2) ..' ст.' end,
vi='bước sang thế kỷ $date2',
['zh-hans']=function(date1, date2, state) return '$date1世纪末' .. Ordinal(date2, 'zh-hans') .. '世纪初' end,
['zh-hant']=function(date1, date2, state) return '$date1世紀末' .. Ordinal(date2, 'zh-hant') .. '世紀初' end,
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='century', era1=state.era1, adj2 = 'early', unit2='century', era2=state.era2} end,
},
-- $date1 - original date - 10
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the decade']={
nDates = 2,
['be-tarask']='канец $date1-х — пачатак $date2-х гг.',
bn='$date1-এর দশকের শেষে এবং $date2-এর দশকের শুরুতে',
cs=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "přelom 1. a 2. dekády " .. century1 .. ". století"
elseif decade2 == 0 and date2 < date1 then
return "přelom 9. a 10. dekády " .. century1 .. ". století"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "přelom " .. century1 .. ". a " .. century2 .. ". století"
else
return "přelom " .. decade1 .. "0. a " .. decade2 .. "0. let " .. century1 .. ". století"
end
end,
de='späte $date1er und frühe $date2er',
en='late $date1s and early $date2s',
['en-gb']='late $date1s and early $date2s',
['en-ca']='late $date1s and early $date2s',
eo='ŝanĝo de la $date1-a al la $date2-a jardekoj',
es='finales de los años $date1 y principios de los años $date2',
fa='اواخر دههٔ $date1 و اوایل دههٔ $date2',
fi='$date1-luvun loppu ja $date2-luvun alku',
fr='fin des années $date1 / début des années $date2',
hr='kraj $date1-ih i početak $date2-ih',
hsb='pózdnje $date1te a zažne $date2te lěta',
it='fine degli anni $date1 / inizio degli anni $date2',
ja='$date1年代末から$date2年代初頭',
ml='$date1കൾ അവസാനവും $date2കൾ ആദ്യവും',
nb = 'sent på $date1-tallet og tidlig på $date2-tallet',
nl = 'eind $date1e/begin $date2e decennium',
nn = 'seint på $date1-talet og tidleg på $date2-talet',
pl=function(date1, date2, state)
local decade1 = math.floor(date1 % 100)
local decade2 = math.floor(date2 % 100)
local century1 = Roman(math.floor( date1 / 100) + 1)
local century2 = Roman(math.floor( date2 / 100) + 1)
if decade1 == 0 and date1 < date2 then
return "przełom pierwszej i drugiej dekady " .. century1 .. " wieku"
elseif decade1 == 10 and date1 < date2 then
return "przełom drugiej dekady i lat 20. " .. century1 .. " wieku"
elseif decade1 == 90 and decade2 == 0 then
return "przełom ostatniej dekady " .. century1 .. " i pierwszej dekady " .. century2 .. " wieku"
else
return "przełom lat " .. decade1 .. ". i " .. decade2 .. ". " .. century1 .. " wieku"
end
end,
qu='qhipaq $date1 watakuna ñawpaq $date2 watakunapas',
ru='конец $date1-х — начало $date2-х гг.',
sv='sena $date1-talet och tidiga $date2-talet',
sk=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "prelom 1. a 2. desaťročia " .. century1 .. ". storočia"
elseif decade2 == 0 and date2 < date1 then
return "prelom 9. a 10. desaťročia " .. century1 .. ". storočia"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "prelom " .. century1 .. ". a " .. century2 .. ". storočia"
else
return "prelom " .. decade1 .. "0. a " .. decade2 .. "0. rokov " .. century1 .. ". storočia"
end
end,
sl='pozna $date1-ta in zgodnja $date2-ta leta',
uk='кінець $date1-х — початок $date2-х рр.',
ur='اخیر دہائی $date1 و ابتدا دہائی $date2',
vi='bước sang thập niên $date2',
['zh-hans']='$date1年代末$date2年代初',
['zh-hant']='$date1年代末$date2年代初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='decade', era1=state.era1, adj2 = 'early', unit2='decade', era2=state.era2} end,
},
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the year']={
nDates = 2,
['be-tarask']='канец $date1 — пачатак $date2 г.',
bn='$date1-এর শেষে এবং $date2-এর শুরুতে',
cs='přelom roku $date1/$date2',
de='Jahreswechsel $date1/$date2',
en='late $date1 and early $date2',
['en-gb']='late $date1 and early $date2',
['en-ca']='late $date1 and early $date2',
eo='jarŝanĝo $date1 / $date2',
es='finales de $date1 y principios de $date2',
fa='اواخر $date1 و اوایل $date2',
fi='vuoden $date1 loppu ja vuoden $date2 alku',
fr='fin $date1 / début $date2',
hsb='kónc $date1/spočatk $date2',
ja='$date1年末から$date2年初め',
ml='$date1 അവസാനവും $date2 ആദ്യവും',
nb='årsskiftet $date1/$date2',
nl='eind $date1 en begin $date2',
nn='årsskiftet $date1/$date2',
pl='koniec $date1 i początek $date2 roku',
qu="$date1 qhipaq p'unchawkuna $date2 ñawpaq p'unchawkunapas",
ru='конец $date1 — начало $date2 г.',
sk='prelom rokov $date1/$date2',
sl='konec leta $date1 in začetek $date2',
sv='sena $date1 och tidiga $date2',
uk='кінець $date1 — початок $date2 р.',
ur='اخیر $date1 و ابتدا $date2',
vi='bước sang năm $date2',
['zh-hans']='$date1年末$date2年初',
['zh-hant']='$date1年末$date2年初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='year', era1=state.era1, adj2 = 'early', unit2='year', era2=state.era2} end,
},
['circa2'] = {
nDates = 2,
ar='حوالي $date1 – $date2',
['be-tarask']={'прыкладна з $date1 да прыкладна $date2', case={'gen','gen'}},
bn='প্রায় $date1 এবং প্রায় $date2-এর মাঝামাঝি',
ca='cap a $date1 o $date2',
cs='cca $date1 až $date2',
da='ca. mellem $date1 og $date2',
de='etwa $date1 bis $date2',
el='περίπου μεταξύ $date1 και $date2',
en='between circa $date1 and circa $date2',
['en-gb']='between circa $date1 and circa $date2',
['en-ca']='between circa $date1 and circa $date2',
eo='proksimume de $date1 ĝis $date2',
es='hacia $date1 o $date2',
et='ca. $date1 ja $date2 vahel',
fa='حدوداً بین $date1 و $date2',
fi={function(date1, date2, state) return 'suunnilleen ' .. decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='vers $date1-$date2',
gl='entre $date1 e $date2',
he='בסביבות ה- $date1 עד $date2',
hr='oko između $date1 i $date2',
hsb={'wot něhdźe $date1 do něhdźe $date2', case={'gen','gen'}},
hu='$date1 és $date2 között',
it='tra il $date1 e il $date2 circa',
ja='$date1頃と$date2頃の間',
mk='~ помеѓу $date1 и $date2',
ml='ഉദ്ദേശം $date1-നും ഉദ്ദേശം $date2-നും ഇടയ്ക്ക്',
nb='ca. mellom $date1 og $date2',
nn='ca. mellom $date1 og $date2',
nds='üm un bi $date1 bet $date2',
nl='ca. $date1 tot $date2',
no='ca. mellom $date1 og $date2',
pl={'od około $date1 do około $date2', case={'gen','gen'}},
pt='cerca de $date1 a $date2',
qu="$date1 p'unchawmantachá $date2 p'unchawkamachá",
ro='circa $date1 – $date2',
ru={'с около $date1 до, примерно, $date2', case={'gen','gen'}},
sk='cca $date1 až $date2',
sl={function(date1, date2, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1 do $date2' end, case={'gen','gen'}},
sv='c:a $date1 till $date2',
uk={'з близько $date1 до, приблизно, $date2', case={'gen','gen'}},
ur='قریباً بین $date1 و $date2',
vi='giữa vào khoảng $date1 và vào khoảng $date2',
['zh-hans']='约$date1~$date2间',
['zh-hant']='約$date1~$date2間',
default=function(date1, date2, state) return {conj='between', adj1 = 'circa', unit1=state.unit1, era1=state.era1, adj2 = 'circa', unit2=state.unit2, era2=state.era2} end,
},
-- ==========================================
-- === Eras and calendars ===================
-- ==========================================
['julian'] = {
nDates = 2,
ar='$date1 ($date2 حسب [[w:ar:التقويم اليولياني|تقويم يولياني]])',
['be-tarask']='$date1 ($date2 паводле [[w:be-tarask:Юліянскі каляндар|ст. ст.]]',
bn='$date1 (জুলিয়ান ক্যালেন্ডারে $date2)',
ca='$date1 ($date2 segons el [[w:ca:Calendari julià|calendari julià]])',
cs='$date1 ($date2 podle [[w:cs:Juliánský kalendář|juliánského kalendáře]])',
da='$date1 ($date2 [[w:da:Julianske kalender|Julianske kalender]])',
de='$date1 ($date2 [[w:de:Julianischer Kalender|Julianischer Kalender]])',
el='$date1 ($date2 με το [[w:el:Ιουλιανό ημερολόγιο|Ιουλιανό ημερολόγιο]])',
en='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-gb']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-ca']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
fa='$date1 ($date2 در [[w:fa:Old Style and New Style dates|تقویم جولیان]])',
eo='$date1 ($date2 laŭ la [[w:eo:Julia kalendaro|Julia Kalendaro]])',
es='$date1 ($date2 según el [[w:es:Calendario juliano|calendario juliano]])',
et='$date1 ($date2 [[w:et:Juliuse kalender|Juliuse kalendris]])',
fi='$date1 ($date2 [[w:fi:juliaaninen kalenteri|juliaanisen kalenterin]] mukaan)',
fr='$date1 ($date2 dans le [[w:fr:calendrier julien|calendrier julien]])',
gl='$date1 ($date2 segundo o [[w:gl:Calendario xuliano|calendario xuliano]])',
hu='$date1 ($date2 a [[w:hu:Julianus-naptár|Julián naptárban]])',
hr='$date1 ($date2 prema [[w:hr:Julijanski kalendar|julijanskom kalendaru]])',
he='$date1 ($date2 ב[[w:he:לוח היוליאני|הלוח היוליאני]])',
hsb='$date1 ($date2 po julianiskej protyce)',
it='$date1 ($date2 secondo il [[w:it:Calendario giuliano|calendario giuliano]])',
ja='$date1([[w:ja:ユリウス暦|ユリウス暦]] $date2)',
mk='$date1 ($date2 по [[w:mk:Јулијански календар|Јулијанскиот календар]])',
ml='$date1 ([[w:ml:ജൂലിയൻ കാലഗണനാരീതി|ജൂലിയൻ വർഷത്തിൽ]] $date2)',
ml='$date1 ([[w:Old Style and New Style dates|ജൂലിയൻ കാലഗണനാരീതിയിൽ]] $date2)',
nds='$date1 ($date2 na’n [[w:nds:Juliaansch Klenner|Juliaanschen Klenner]])',
nb='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nn='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nl='$date1 ($date2 volgens de [[w:nl:Juliaanse kalender|Juliaanse kalender]])',
no='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalender]])',
pl='$date1 ($date2 wedle [[w:pl:Daty nowego i starego porządku|kalendarza juliańskiego]])',
pt='$date1 ($date2 no [[w:pt:Mudança para o calendário gregoriano|calendário juliano]])',
qu='$date1 ($date2 [[w:qu:Hulyanu kalindaryu|Hulyanu kalindaryupi]])',
ro='$date1 ($date2 în [[w:ro:calendarul iulian|calendarul iulian]])',
ru='$date1 ($date2 [[w:ru:Юлианский календарь|по ст. ст.]])',
sk='$date1 ($date2 podľa [[w:sk:Juliánsky kalendár|juliánskeho kalendára]])',
sl='$date1 ($date2 po [[w:sl:Julijanski koledar|julijanskem koledarju]])',
sv='$date1 ($date2 [[w:sv:Gamla stilen|gamla stilen]])',
uk='$date1 ($date2 [[w:uk:Юліанський календар|Юліанський календар]])',
ur='$date1 ($date2 حسب [[w:ur:نئے اور پانے انداز کی تاریخ|جولین تقویم]])',
vi='$date1 ($date2 trong [[w:vi:Lịch Julius|lịch Juliêng]])',
['zh-hans']='$date1([[w:Old Style and New Style dates|格里历]]$date2)',
['zh-hant']='$date1([[w:Old Style and New Style dates|格里曆]]$date2)',
default='<span style="color:red">$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])</span>',
},
['ad'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:ад Нараджэньня Хрыста|пасьля Н. Х.]]',
bn ='$date1 খ্রিস্টাব্দ',
ca ='$date1 dC',
cs ='$date1 n. l.',
de ='$date1 n. Chr.',
el ='$date1 μ.Χ.',
en ='$date1 [[w:en:Anno Domini|AD]]',
['en-gb'] ='$date1 [[w:en:Anno Domini|AD]]',
['en-ca'] ='$date1 [[w:en:Anno Domini|AD]]',
eo ='$date1 pK',
es ='$date1 [[w:es:Anno Dómini|d. C.]]',
fa ='$date1 م.',
fi ='$date1 jaa.',
fr ='$date1 ap. J.-C.',
gl ='$date1 d.C.',
he ='$date1 לספירה',
hu ='i. sz. $date1',
hr ='$date1 p. Kr.',
hsb='$date1 po Chr.',
it ='$date1 d.C.',
ja ='西暦$date1',
mk ='$date1 н.е.',
ml ='ക്രി.വ. $date1',
nb ='$date1 e.Kr.',
nl ='$date1 n.Chr.',
nn ='$date1 e.Kr.',
no ='$date1 e.Kr.',
pl ='$date1 [[w:pl:naszej ery|n.e.]]',
ro ='$date1 d.Hr.',
ru ='$date1 н. э.',
sk ='$date1 po Kr.',
sl ='$date1 n. št.',
sv ='$date1 [[w:sv:Efter Kristus|e.Kr.]]',
uk ='$date1 н. е.',
ur ='$date1 ء',
vi ='$date1 [[w:vi:Công Nguyên|CN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元]]$date1',
default='<span style="color:red">$date1 [[w:en:Anno Domini|AD]]</span>',
},
['ah'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:Ісламскі каляндар|хіджры]]',
bn ='$date1 [[w:bn:ইসলামি বর্ষপঞ্জি|হিজরী]]',
cs ='$date1 [[w:cs:Anno Hegirae|AH]]',
en ='$date1 [[w:en:Hijri year|AH]]',
['en-gb'] ='$date1 [[w:en:Hijri year|AH]]',
['en-ca'] ='$date1 [[w:en:Hijri year|AH]]',
eo ='$date1 post la [[w:eo:Heĝiro|Heĝiro]]',
fa ='$date1 ه.ق.',
fr ='$date1 [[w:fr:Calendrier musulman|A.H.]]',
he ="$date1 [[w:he:הלוח המוסלמי|להג'רה]]",
hr ='$date1 god. hidžre',
ja ='[[w:ja:ヒジュラ暦|ヒジュラ暦]]$date1',
ml ='$date1 [[w:ml:ഇസ്ലാമിക കലണ്ടർ|ഹിജ്റ]]',
nb ='$date1 [[w:no:Den muslimske kalenderen|A.H.]]',
nn ='$date1 [[w:nn:Den muslimske kalenderen|A.H.]]',
pl ='$date1 [[w:pl:Kalendarz muzułmański|A.H.]]',
ru ='$date1 [[w:ru:Исламский календарь|хиджры]]',
sk ='$date1 [[w:sk:A. H.|A. H.]]',
sv ='$date1 [[w:sv:Muslimska kalendern|A.H.]]',
uk ='$date1 [[w:uk:Мусульманський календар|хіджри]]',
ur ='$date1 ھ',
vi ='$date1 [[w:vi:Lịch Hồi giáo|AH]]',
['zh-hans'] ='[[w:en:Hijri year|Hijri历]]$date1',
['zh-hant'] ='[[w:en:Hijri year|Hijri曆]]$date1',
default='<span style="color:red">$date1 [[w:en:Hijri year|AH]]</span>',
},
['bc'] = {
nDates = 1,
ar ='$date1 ق.م.',
['be-tarask']='$date1 [[w:be-tarask:да Нараджэньня Хрыста|да Н. Х.]]',
bn ='খ্রিস্টপূর্ব $date1',
ca ='$date1 [[w:ca:Abans de Crist|aC]]',
cs ='$date1 [[w:cs:Př. n. l.|př. n. l.]]',
de ='$date1 [[w:de:v. Chr.|v. Chr.]]',
el ='$date1 π.Χ.',
en ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-gb'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-ca'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
eo ='$date1 aK',
es ='$date1 [[w:es:Antes de Cristo|a. C.]]',
fa ='$date1 پ.م.',
fi ='$date1 eaa.',
fr ='$date1 av. J.-C.',
gl ='$date1 [[w:gl:Antes de Cristo|a.C.]]',
he ='$date1 לפני הספירה',
hr ='$date1 [[w:hr:Pr. Kr.|pr. Kr.]]',
hsb='$date1 do Chr.',
hu ='i. e. $date1',
it ='$date1 [[w:it:Avanti Cristo|a.C.]]',
ja ='[[w:ja:紀元前|紀元前]]$date1',
mk ='$date1 п.н.е.',
ml ='ക്രി.മു. $date1',
nds='$date1 vör uns Tied',
nb ='$date1 f.Kr.',
nl ='$date1 v.Chr.',
nn ='$date1 f.Kr.',
no ='$date1 f.Kr.',
pl ='$date1 [[w:pl:przed naszą erą|p.n.e.]]',
ro ='$date1 î.Hr.',
ru ='$date1 до н. э.',
sk ='$date1 [[w:sk:Pred Kristom|pred Kr.]]',
sl ='$date1 pr. n. št.',
sv ='$date1 [[w:sv:Före Kristus|f.Kr.]]',
uk ='$date1 до н. е.',
ur ='$date1 ق م',
vi ='$date1 [[w:vi:Trước Công Nguyên|TCN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元前]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元前]]$date1',
default='<span style="color:red">$date1 [[w:en:Ante Christum Natum|BC]]</span>',
},
['bp'] = {
nDates = 1,
ar ='$date1 [[w:ar:قبل الحاضر|قبل الحاضر]]',
be ='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
['be-tarask']='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
bn ='বর্তমানের আগে $date1',
ca ='$date1 [[w:ca:BP (unitat de temps)|BP]]',
cs ='$date1 [[w:cs:B. P.|B. P.]]',
de ='$date1 [[w:de:Before Present|BP]]',
el ='$date1 [[w:en:Before Present|BP]]',
en ='$date1 [[w:en:Before Present|BP]]',
['en-gb'] ='$date1 [[w:en:Before Present|BP]]',
['en-ca'] ='$date1 [[w:en:Before Present|BP]]',
eo='$date1 [[w:eo:Before Present|BP]]',
es ='$date1 [[w:es:Before Present|BP]]',
et ='$date1 [[w:et:Before Present|BP]]',
fa ='$date1 پیش از اکنون',
fi ='$date1 [[w:fi:BP (ajanlasku)|BP]]',
fr ='$date1 [[w:fr:Avant le présent|avant le présent]]',
hr ='$date1 [[w:hr:BP|prije današnjice]]',
it ='$date1 [[w:it:Before Present|BP]]',
ja ='$date1 [[w:ja:BP (年代測定)|BP]]',
lv ='$date1 [[w:lv:Pirms mūsdienām|Pirms mūsdienām]]',
mk ='$date1 [[w:mk:Пред сегашноста|п.с.]]',
ml ='[[w:en:Before Present|ഇന്നത്തെ കാലത്തിനു മുമ്പ്]] $date1',
nb ='$date1 [[w:no:Before Present|BP]]',
nn ='$date1 [[w:nn:Before Present|BP]]',
nl ='$date1 [[w:nl:Before Present|BP]]',
no ='$date1 [[w:no:Before Present|BP]]',
pl ='$date1 [[w:pl:Before Present|BP]]',
pt ='$date1 [[w:pt:Antes do Presente|Antes do Presente]]',
ru ='$date1 [[w:ru:До настоящего времени|до н. в.]]',
sk ='$date1 [[w:sk:BP (čas)|BP]]',
sl ='$date1 [[w:sl:pred sedanjostjo|p. s.]]',
sr ='$date1 [[w:sr:Прије садашњости|Прије садашњости]]',
sv ='$date1 [[w:sv:B.P.|B.P.]]',
uk ='$date1 [[w:uk:Років тому|Років тому]]',
ur ='$date1 قبل حاضر',
vi ='$date1 [[w:en:Before Present|trước ngày nay]]',
['zh-hans'] ='[[w:zh:距今|距今]]$date1',
['zh-hant'] ='[[w:zh:距今|距今]]$date1',
default='<span style="color:red">$date1 [[w:en:Before Present|BP]]</span>',
},
}
-- List of possible string inputs to "adj", "junction", "era", and "precision" fields
-- Each term is matched to its synonym and the role: a - adjective, e - era
-- p - precision, c - calendar and combos, r - certainty and j - conjunction
p.Synonyms = {
-- adjectives and prepositions
['quarter1']={'1quarter','a'},
['1stquarter']={'1quarter','a'},
['1st quarter']={'1quarter','a'},
['first quarter']={'1quarter','a'},
['1quarter']={'1quarter','a'},
['quarter2']={'2quarter','a'},
['2ndquarter']={'2quarter','a'},
['2nd quarter']={'2quarter','a'},
['second quarter']={'2quarter','a'},
['2quarter']={'2quarter','a'},
['quarter3']={'3quarter','a'},
['3rdquarter']={'3quarter','a'},
['3rd quarter']={'3quarter','a'},
['third quarter']={'3quarter','a'},
['3quarter']={'3quarter','a'},
['quarter4']={'4quarter','a'},
['4thquarter']={'4quarter','a'},
['4th quarter']={'4quarter','a'},
['fourth quarter']={'4quarter','a'},
['4quarter']={'4quarter','a'},
['spring']={'spring','a'},
['summer']={'summer','a'},
['fall']={'autumn','a'},
['autumn']={'autumn','a'},
['winter']={'winter','a'},
['beginning']={'early','a'},
['early']={'early','a'},
['1half']={'firsthalf','a'},
['1sthalf']={'firsthalf','a'},
['1st half']={'firsthalf','a'},
['firsthalf']={'firsthalf','a'},
['first half']={'firsthalf','a'},
['end']={'late','a'},
['late']={'late','a'},
['middle']={'mid','a'},
['mid']={'mid','a'},
['2half']={'secondhalf','a'},
['2ndhalf']={'secondhalf','a'},
['2nd half']={'secondhalf','a'},
['secondhalf']={'secondhalf','a'},
['second half']={'secondhalf','a'},
['<']={'before','a'},
['b']={'before','a'},
['before']={'before','a'},
['>']={'after','a'},
['a']={'after','a'},
['after']={'after','a'},
['~']={'circa','a'},
['c']={'circa','a'},
['ca']={'circa','a'},
['circa']={'circa','a'},
['by']={'by','a'},
['from']={'from','a'},
['until']={'until','a'},
['uncertain']={'uncertain','r'},
['possibly']={'possibly','r'},
['probably']={'probably','r'},
['presumably']={'presumably','r'},
-- eras
['bc']={'bc','e'},
['bce']={'bc','e'},
['before present']={'bp','e'},
['bp']={'bp','e'},
['ce']={'ad','e'},
['ad']={'ad','e'},
['ah']={'ah','e'},
-- conjunctions
['&']={'and','j'},
['and']={'and','j'},
['or']={'or','j'},
['-']={'from-until','j'},
['–']={'from-until','j'},
['from-until']={'from-until','j'},
['between']={'between','j'},
['circa2']={'circa2','a'},
-- precision
['day']={'day','p'},
['month']={'month','p'},
['year']={'year','p'},
['s']={'decade','p'},
['decade']={'decade','p'},
['century']={'century','p'},
['millennium']={'millennium','p'},
['million']={'million','p'},
['billion']={'billion','p'},
-- calendar
['julian']={'julian','c'},
['islamic']={'islamic','c'},
['hijri']={'islamic','c'},
-- "Turn of the ..." options are a combos which will be handled in similar way as calendars
['turn of the century']={'turn of the century','c'},
['century turn']={'turn of the century','c'},
['turn of the decade']={'turn of the decade','c'},
['decade turn']={'turn of the decade','c'},
['turn of the year']={'turn of the year','c'},
['year turn']={'turn of the year','c'},
-- other dates which will be handled in similar way as calendars
['?']={'unknown','c'},
['unknown']={'unknown','c'},
['unknown year']={'year unknown','c'},
['year unknown']={'year unknown','c'},
}
return p
g9dzfs88xlwnkv4sku4bi0qkysnjj4o
803607
803606
2019-02-24T14:30:15Z
en>RexxS
0
call Module:Ordinal-cd, which is a copy of the version used on Commons
803607
Scribunto
text/plain
--[[
__ __ _ _ ___ _ ___ __ _ _ _
| \/ | ___ __| |_ _| | ___ _|_ _/ |( _ ) _ __ / /__ ___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_)| || |/ _ \| '_ \ / / __/ _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_ | || | (_) | | | |/ / (_| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)___|_|\___/|_| |_/_/ \___\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This code was copied from Commons please request changes there.
Maintainers:
*Jarekt
]]
local Ordinal = require('Module:Ordinal-cd')._Ordinal
local Roman = require('Module:Roman')._Numeral
local linguistic = require('Module:Linguistic')
-- ==========================================================================
-- === Local Helper functions ==============================================
-- ==========================================================================
-- form of French "of" based on "precision", which is wikibase compatible date precision format:
-- 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- 1 2 3 4 5 mill cent deca year month day
local frOf = {'du','du','du','du','du', 'du', 'du', 'de', 'de', 'de', 'du'}
local function decYear(state,lang,case)
--Declension of word "year" in several languages
if state.precision == 9 then
yearCase = {
fi={xxx='vuosien', gen='vuoden', ill='vuoteen', ptv='vuotta', ela='vuodesta', ine='vuonna'},
sl={insP='letoma', ins='letom', gen='leta', loc='letu'},
hsb={insP='lětami', ins='lětom', gen='lěta', loc='lěće'}
}
return yearCase[lang][case] .. ' '
else
return ''
end
end
local function decCentury(lang,case)
--Declension of word "century" in several languages
centuryCase = {
pl={nom='wiek', gen='wieku', ins='wiekiem', loc='wieku', default = 'wiek'},
hsb={nom='lětstotk', gen='lětstotka', ins='lětstotkom', loc='lětstotku', default = 'lětstotk'}
}
return centuryCase[lang][case] or centuryCase[lang]['default']
end
local function decMillennium(lang,case)
--Declension of word "century" in several languages
millenniumCase = {
pl={nom='tysiąclecie', gen='tysiąclecia', ins='tysiącleciem', loc='tysiącleciu', default = 'tysiąclecie'},
hsb={nom='lěttysac', gen='lěttysaca', ins='lěttysacom', loc='lěttysacu', default = 'lěttysac'}
}
return millenniumCase[lang][case] or millenniumCase[lang]['default']
end
-- ==========================================================================
-- === Output data structure ===============================================
-- ==========================================================================
local p = {}
--[[
Multiple formats are possible:
1) string with key words "$date1" and "$date2" which will be replaced later by the date strings
2) function which can be used to create output string
* Function INPUTS:
- date1 and date2 string with date strings. "$date1" and "$date2" can also be used which will be replaced latter
- state is a table with full description of the phrase we are trying to construct. It includes state.precision1 (and 2)
which is wikibase compatible date precision format: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
3) table with following fields:
* table[1] is either string or a function
* table.case is a 2 element table with Grammatical cases used by given language to alter
month name or other adjective related to date # 1 and 2
]]
p.Translations = {
-- ==========================================
-- === Two date phrases =====================
-- ==========================================
['from-until'] = {
nDates = 2,
ar='من $date1 إلى $date2',
['be-tarask']={'з $date1 да $date2', case={'gen','gen'}},
bn='$date1 থেকে $date2 পর্যন্ত',
ca='$date1 fins $date2',
cs='$date1 až $date2',
da='fra $date1 til $date2',
de='$date1 bis $date2',
el='από $date1 έως $date2',
en='from $date1 until $date2',
['en-gb']='from $date1 until $date2',
['en-ca']='from $date1 until $date2',
eo='de $date1 al $date2',
es='de $date1 a $date2',
et='$date1 kuni $date2',
fa='از $date1 تا $date2',
fi='$date1–$date2', -- välisenä aikana
fr=function(date1, date2, state) -- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision > 10 then -- precision > month
return 'du $date1 au $date2'
else -- "DE septebmbre" / "D'octobre"
return linguistic.of(date1, 'fr') .. " à $date2"
end
end,
gl='de $date1 a $date2', --Template:From until used 'desde $date1 ata $date2'
he='מ- $date1 עד $date2',
hr='od $date1 do $date2',
hsb={'wot $date1 do $date2', case={'gen','gen'}},
hu='$date1 – $date2',
it='dal $date1 al $date2',
ja='$date1から$date2まで',
mk='од $date1 до $date2',
ml='$date1 മുതൽ $date2 വരെ',
nb='fra $date1 til $date2',
nds='$date1 bet $date2',
nn='fra $date1 til $date2',
nl='$date1 tot $date2',
no='fra $date1 til $date2',
pl={'od $date1 do $date2', case={'gen','gen'}},
ro='din $date1 până în $date2',
pt='de $date1 a $date2',
qu="$date1 p'unchawmanta $date2 p\'unchawkama",
ru={'с $date1 по $date2', case={'gen','gen'}},
sk={'od $date1 do $date2', case={'gen','gen'}},
sl={'od $date1 do $date2', case={'gen','gen'}},
sv='från $date1 till $date2',
uk={'з $date1 до $date2', case={'gen','gen'}},
ur='از $date1 تا $date2',
vi='từ $date1 đến $date2',
['zh-hans']='从$date1到$date2',
['zh-hant']='從$date1到$date2',
default='<span style="color:red">$date1 - $date2</span>',
},
['between']={
nDates = 2,
ar='بين $date1 و $date2',
['be-tarask']={'між $date1 і $date2', case={'ins','ins'}},
bn='$date1 এবং $date2-এর মাঝামাঝি',
ca='entre $date1 i $date2',
cs={'mezi $date1 a $date2', case={'ins','ins'}},
da='mellem $date1 og $date2',
de='zwischen $date1 und $date2',
el='μεταξύ $date1 και $date2',
en='between $date1 and $date2',
['en-gb']='between $date1 and $date2',
['en-ca']='between $date1 and $date2',
eo='inter $date1 kaj $date2',
es='entre $date1 y $date2',
et='$date1 ja $date2 vahel',
fa='بین $date1 و $date2',
fi={function(date1, date2, state) return decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='entre $date1 et $date2',
gl='entre $date1 e $date2',
he='בין $date1 ו- $date2',
hr='između $date1 i $date2',
hsb={'mjez $date1 a $date2', case={'ins','ins'}},
hu='$date1 és $date2 között',
hi='$date1 और $date2 के बीच',
it='tra il $date1 e il $date2',
ja='$date1と$date2の間',
mk='помеѓу $date1 и $date2',
ml='$date1, $date2 എന്നിവയ്ക്കിടയ്ക്ക്',
nb='mellom $date1 og $date2',
nds='twischen $date1 un $date2',
nn='mellom $date1 og $date2',
nl='tussen $date1 en $date2',
no='mellom $date1 og $date2',
pl={'między $date1 a $date2', case={'ins','ins'}},
ro='între $date1 și $date2',
pt='entre $date1 e $date2',
qu="$date1 p'unchawpa $date2 p'unchawpapas chawpin",
ru={'между $date1 и $date2', case={'ins','ins'}},
sk={'medzi $date1 a $date2', case={'ins','ins'}},
sl={function(date1, date2, state) return 'med ' .. decYear(state,'sl','insP') .. '$date1 in $date2' end, case={'ins','ins'}},
sv='mellan $date1 och $date2',
uk={'між $date1 та $date2', case={'ins','ins'}},
ur='بین $date1 و $date2',
vec='tra el $date1 e el $date2',
vi='giữa $date1 và $date2',
['zh-hans']='在$date1到$date2之间',
['zh-hant']='在$date1到$date2之間',
default='<span style="color:red">$date1 - $date2</span>',
},
['or'] = {
nDates = 2,
ar='$date1 أو $date2',
['be-tarask']='$date1 ці $date2',
bn='$date1 অথবা $date2',
ca='$date1 o $date2',
cs='$date1 nebo $date2',
da='$date1 eller $date2',
de='$date1 oder $date2',
el='$date1 ή $date2',
en='$date1 or $date2',
['en-gb']='$date1 or $date2',
['en-ca']='$date1 or $date2',
eo='$date1 aŭ $date2',
es='$date1 o $date2',
et='$date1 või $date2',
fa='$date1 یا $date2',
fi='$date1 tai $date2',
fr='$date1 ou $date2',
gl='$date1 ou $date2',
he='$date1 או $date2',
hr='$date1 ili $date2',
hsb='$date1 abo $date2',
hu='$date1 vagy $date2',
it='$date1 o $date2',
ja='$date1または$date2',
mk='$date1 или $date2',
ml='$date1 അഥവാ $date2',
nb='$date1 eller $date2',
nn='$date1 eller $date2',
nds='$date1 oder $date2',
nl='$date1 of $date2',
no='$date1 eller $date2',
pl='$date1 lub $date2',
pt='$date1 ou $date2',
qu='$date1 icha $date2',
ro='$date1 sau $date2',
ru='$date1 или $date2',
sk='$date1 alebo $date2',
sl='$date1 ali $date2',
sv='$date1 eller $date2',
uk='$date1 або $date2',
ur='$date1 یا $date2',
th='$date1 หรือ $date2',
vi='$date1 hoặc $date2',
['zh-hans']='$date1或$date2',
['zh-hant']='$date1或$date2',
default=function(date1, date2, state) return linguistic.conj({date1, date2}, state.lang, 'or') end,
},
['and'] = {
nDates = 2,
ar='$date1 و $date2',
['be-tarask']='$date1 і $date2',
bn='$date1 এবং $date2',
ca='$date1 i $date2',
cs='$date1 a $date2',
da='$date1 og $date2',
de='$date1 und $date2',
el='$date1 και $date2',
en='$date1 and $date2',
['en-gb']='$date1 and $date2',
['en-ca']='$date1 and $date2',
eo='$date1 kaj $date2',
es='$date1 y $date2',
et='$date1 ja $date2',
fa='$date1 و $date2',
fi='$date1 ja $date2',
fr='$date1 et $date2',
gl='$date1 e $date2',
he='$date1 ו- $date2',
hu='$date1 és $date2',
hsb='$date1 a $date2',
it='$date1 e $date2',
ja='$date1および$date2',
mk='$date1 и $date2',
ml='$date1 ഒപ്പം $date2',
nb='$date1 og $date2',
nn='$date1 og $date2',
nds='$date1 un $date2',
nl='$date1 en $date2',
no='$date1 og $date2',
pl='$date1 i $date2',
pt='$date1 e $date2',
qu='$date1wan $date2pas',
ro='$date1 și $date2',
ru='$date1 и $date2',
sk='$date1 a $date2',
sl='$date1 in $date2',
sv='$date1 och $date2',
uk='$date1 та $date2',
ur='$date1 و $date2',
vi='$date1 và $date2',
['zh-hans']='$date1和$date2',
['zh-hant']='$date1和$date2',
default=function(date1, date2, state) return linguistic.conj({date1, date2}, state.lang, 'and') end,
},
-- ==========================================
-- === Prepositions =========================
-- ==========================================
['by'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'да $date1', case={'gen',''}},
bn='$date1 কর্তৃক',
ca='vers $date1',
cs={'kolem $date1', case={'gen',''}},
da='mod $date1',
de='spätestens $date1',
el='έως $date1',
en='by $date1',
['en-gb']='by $date1',
['en-ca']='by $date1',
eo='ne post $date1',
es='para $date1',
et='hiljemalt $date1',
fa='توسط $date1',
fi={function(date1, state) return decYear(state,'fi','ill') .. '$date1 mennessä' end, case={'ill',''}},
fr='$date1 (au plus tard)',
gl='cara a $date1',
he='עד $date1',
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='entro il $date1',
ja='$date1まで',
mk='кон $date1',
ml='$date1-ൽ',
nb='innen $date1',
nn='innan $date1',
nds='$date1 oder vördem al',
nl='tegen $date1',
no='innen $date1',
pl={'przed $date1', case={'ins',''}},
pt='por $date1',
qu="$date1 p'unchawkama",
ro='de $date1',
ru={'к $date1', case={'dat',''}},
sk={'okolo $date1', case={'gen',''}},
sl={function(date1, state) return 'najpozneje ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='senast $date1',
uk={'до $date1', case={'dat',''}},
ur='توسط $date1',
vi='vào $date1', -- need to insert "đã" somewhere later in the sentence -->
['zh-hans']='$date1前后',
['zh-hant']='$date1前后',
default='<span style="color:red">by $date1</span>',
},
['before'] = {
nDates = 1,
ar='قبل $date1',
['be-tarask']={'перад $date1', case={'ins',''}},
bn='$date1-এর আগে',
bg='Преди $date1',
ca='abans $date1',
cs={'před $date1', case={'ins',''}},
da='før $date1',
de='vor $date1',
el='πριν από $date1',
en='before $date1',
['en-gb']='before $date1',
['en-ca']='before $date1',
eo='antaŭ $date1',
es='antes de $date1',
et='enne $date1',
fa='پیش از $date1',
fi={function(date1, state) return 'ennen ' .. decYear(state,'fi','ptv') .. '$date1' end, case={'ptv',''}},
fr='avant $date1',
gl='antes do $date1',
he='לפני $date1',
hr='prije $date1',
hsb={'do $date1', case={'gen',''}},
hu='$date1 előtt',
it='prima del $date1',
ja='$date1より前',
mk='пред $date1',
ml='$date1-നു മുമ്പ്',
nb='før $date1',
nn='før $date1',
nds='vör $date1',
nl='voor $date1',
no='før $date1',
pl={'przed $date1', case={'ins',''}},
pt='antes de $date1',
qu="$date1 p'unchaw ñawpan",
ro='înainte de $date1',
ru={'ранее $date1', case={'gen',''}},
sk={'pred $date1', case={'ins',''}},
sl={function(date1, state) return 'pred ' .. decYear(state,'sl','ins') .. ' $date1' end, case={'ins',''}},
sv='före $date1',
uk={'до $date1', case={'gen',''}},
ur='قبل $date1',
vi='trước $date1',
['zh-hans']='在$date1之前',
['zh-hant']='在$date1之前',
default='<span style="color:red">before $date1</span>',
},
['from'] = {
nDates = 1,
ar='من $date1',
['be-tarask']={'з $date1', case={'gen',''}},
bn='$date1 থেকে',
cs={'od $date1', case={'gen',''}},
de='Seit $date1',
el='από $date1',
en='from $date1',
['en-gb']='from $date1',
['en-ca']='from $date1',
eo='ekde $date1',
es='desde $date1',
et='alates $date1',
fa='از $date1',
fi={function(date1, state) return decYear(state,'fi','ela') .. '$date1 alkaen' end, case={'ela',''}},
fr= function(date1, state) return 'à partir '.. frOf[state.precision] ..' $date1' end,
gl='desde o $date1',
he='מ- $date1',
hr='od $date1',
hsb={'wot $date1', case={'gen',''}},
it='dal $date1',
ja='$date1から',
nds='vonaf $date1',
mk='од $date1',
nb='fra $date1',
nn='frå $date1',
ml='$date1 മുതൽ',
nl='vanaf $date1',
no='fra $date1',
pl={'od $date1', case={'gen',''}},
qu="$date1 p'unchawmanta",
ro='din $date1',
ru={'С $date1', case={'gen',''}},
sk={'od $date1', case={'gen',''}},
sl={function(date1, state) return 'od ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='från $date1',
uk={'з $date1', case={'gen',''}},
ur='از $date1',
vi='từ $date1',
['zh-hans']='自$date1',
['zh-hant']='自$date1',
default='<span style="color:red">from $date1</span>',
},
['until'] = {
nDates = 1,
['be-tarask']={'усутыч да $date1', case={'gen',''}},
bn='$date1 পর্যন্ত',
cs={'do $date1', case={'gen', ''}},
de='bis $date1',
en='until $date1',
['en-gb']='until $date1',
['en-ca']='until $date1',
eo='ĝis $date1',
es='hasta $date1',
fr=function(date1, state)
if state.precision > 10 then -- precision > month
return "jusqu'au $date1"
else
return "jusqu'à $date1"
end
end,
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='al $date1',
nb='til $date1',
nl='tot $date1',
nn='til $date1',
ml='$date1 വരെ',
pl={'do $date1', case={'gen',''}},
ru={'вплоть до $date1', case={'gen',''}},
sk={'do $date1', case={'gen', ''}},
uk={'аж до $date1', case={'gen',''}},
ur='تک $date1',
['zh-hans']='直到$date1',
['zh-hant']='直到$date1',
default='<span style="color:red">until $date1</span>',
},
['after'] = {
nDates = 1,
ar='بعد $date1',
['be-tarask']={'пасьля $date1', case={'gen',''}},
bn='$date1-এর পরে',
ca='després de $date1',
cs={'po $date1', case={'loc',''}},
de='nach $date1',
da='efter $date1',
el='μετά από $date1',
en='after $date1',
['en-gb']='after $date1',
['en-ca']='after $date1',
eo='post $date1',
es='después de $date1',
et='pärast $date1',
fa='پس از $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 jälkeen' end, case={'gen',''}},
fr='après $date1',
gl='despois do $date1',
he='לאחר $date1',
hr='nakon $date1',
hu='$date1 után',
hsb={'po $date1', case={'loc',''}},
it='dopo il $date1',
ja='$date1より後',
mk='по $date1',
ml='$date1-നു ശേഷം',
nb='etter $date1',
nn='etter $date1',
nds='na $date1',
nl='na $date1',
no='etter $date1',
pl={'po $date1', case={'loc',''}},
pt='após $date1',
qu="$date1 p'unchaw qhipan",
ro='după $date1',
ru={'после $date1', case={'gen',''}},
sk={'po $date1', case={'loc',''}},
sl={function(date1, state) return 'po ' .. decYear(state,'sl','loc') .. ' $date1' end, case={'loc',''}},
sv='efter $date1',
uk={'після $date1', case={'gen',''}},
ur='بعد $date1',
vi='sau $date1',
['zh-hans']='在$date1之后',
['zh-hant']='在$date1之後',
default='<span style="color:red">after $date1</span>',
},
-- ==========================================
-- === Adjectives ===========================
-- ==========================================
['early'] = {
nDates = 1,
ar='بداية $date1',
['be-tarask']={'пачатак $date1', case={'gen',''}},
bn='$date1-এর শুরুতে',
ca='al principi de $date1',
cs={'začátek $date1', case={'gen',''}},
da='tidlig $date1',
de='Anfang $date1',
el='αρχές του $date1',
en='early $date1',
['en-gb']='early $date1',
['en-ca']='early $date1',
eo='komence de $date1',
es='a principios de $date1',
et='$date1 algus',
fa='اوایل $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alku' end, case={'gen',''}},
fr='début $date1',
gl='a principios de $date1',
he='תחילת $date1',
hr='početak $date1',
hsb={'spočatk $date1', case={'gen',''}},
hu='$date1 elején',
it='inizio del $date1',
ja='$date1初頭',
mk='почеток на $date1',
ml='$date1 ആദ്യപാദം',
nb='tidlig $date1',
nn='tidleg $date1',
nds='Anfang $date1',
nl='begin $date1',
no='tidlig $date1',
pl={'początek $date1', case={'gen',''}},
pt='a princípios de $date1',
qu="$date1 ñawpaq p'unchawkunapi",
ro='începând cu $date1',
ru={'начало $date1', case={'gen',''}},
sk={'začiatok $date1', case={'gen',''}},
sl=function(date1, state)-- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision == 9 then -- precision > month
return 'zgodnje leto $date1'
else -- "DE septebmbre" / "D'octobre"
return 'zgodnji $date1'
end
end,
sv='början av $date1',
uk={'початок $date1', case={'gen',''}},
ur='ابتدا $date1',
vi='đầu năm $date1',
['zh-hans']='$date1早期',
['zh-hant']='$date1早期',
default='<span style="color:red">early $date1</span>',
},
['mid'] = {
nDates = 1,
ar='منتصف $date1',
['be-tarask']={'сярэдзіна $date1', case={'gen',''}},
bn='$date1-এর মাঝামাঝি',
ca='a mitjan $date1',
cs={'polovina $date1', case={'gen',''}},
da='midten af $date1',
de='Mitte $date1',
el='μέσα του $date1',
en='mid $date1',
['en-gb']='mid $date1',
['en-ca']='mid $date1',
eo='meze de $date1',
es='a mediados de $date1',
et='$date1 keskel',
fa='اواسط $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 puoliväli' end, case={'gen',''}},
fr='mi $date1',
gl='a mediados de $date1',
he='אמצע $date1',
hr='sredina $date1',
hsb={'srjedź $date1', case={'gen',''}},
hu='$date1 közepén',
it='metà del $date1',
ja='$date1半ば',
mk='средина на $date1',
ml='$date1 മദ്ധ്യം',
nb='midten av $date1',
nn='midten av $date1',
nds='Midd $date1',
nl='midden $date1',
no='midten av $date1',
pl={'środek $date1', case={'gen',''}},
pt='meados de $date1',
qu="$date1 chawpin p'unchawkunapi",
ro='mijlocul lui $date1',
ru={'середина $date1', case={'gen',''}},
sk={'polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'sredina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='mitten av $date1',
uk={'середина $date1', case={'gen',''}},
ur='وسط $date1',
vi='giữa năm $date1',
['zh-hans']='$date1中期',
['zh-hant']='$date1中期',
default='<span style="color:red">mid $date1</span>',
},
['late'] = {
nDates = 1,
ar='نهاية $date1',
bn='$date1-এর শেষে',
['be-tarask']={'канец $date1', case={'gen',''}},
ca='al final de $date1',
cs='konec $date1',
da='sen $date1',
de='Ende $date1',
el='τέλη του $date1',
en='late $date1',
['en-gb']='late $date1',
['en-ca']='late $date1',
eo='fine de $date1',
es='a fines de $date1',
et='$date1 lõpp',
fa='اواخر $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppu' end, case={'gen',''}},
fr='fin $date1',
gl='a finais de $date1',
he='סוף $date1',
hr='kraj $date1',
hsb={'kónc $date1', case={'gen',''}},
hu='$date1 végén',
it='fine del $date1',
ja='$date1末',
mk='крајот на $date1',
ml='$date1 അന്ത്യപാദം',
nb='sent $date1',
no='seint $date1',
nds='Enn $date1',
nl='eind $date1',
no='sent $date1',
pl={'koniec $date1', case={'gen',''}},
pt='ao final de $date1',
qu="$date1 qhipaq p'unchawkunapi",
ro='sfârșitul lui $date1',
ru={'конец $date1', case={'gen',''}},
sk={'koniec $date1', case={'gen',''}},
sl={function(date1, state) return 'pozno ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='slutet av $date1',
uk={'кінець $date1', case={'gen',''}},
ur='اخیر $date1',
vi='cuối năm $date1',
['zh-hans']='$date1晚期',
['zh-hant']='$date1晚期',
default='<span style="color:red">late $date1</span>',
},
['circa'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'каля $date1', case={'gen',''}},
bn='প্রায় $date1',
ca='cap a $date1',
cs='cca $date1',
da='ca. $date1',
de='etwa $date1',
el='περ. $date1',
en='circa $date1',
['en-gb']='circa $date1',
['en-ca']='circa $date1',
eo='ĉirkaŭ $date1',
es='hacia $date1', -- suggestion use "hacia"/"hacia el" or leave the latin circa or ca.
et='ca. $date1',
fa='حوالی $date1', -- template:Circa used 'حدود $date1'
fi={function(date1, state) return 'suunnilleen ' .. decYear(state,'fi','ine') .. '$date1' end, case={'ine',''}}, -- template:Circa used 'noin $date1'
fr=function(date1, state)
if state.precision >= 11 then -- precision >= day
return 'vers le $date1'
else
return 'vers $date1'
end
end,
gl='entre $date1', -- template:Circa used 'cara a $date1'
he='בסביבות $date1',
hr='oko $date1',
hsb={'wokoło $date1', case={'gen',''}},
hu='$date1 körül',
it='$date1 circa',
ja='$date1頃',
ko='$date1경',
mk='~ $date1', -- template:Circa used 'o. $date1'
ml= 'ഉദ്ദേശം $date1',
nb='ca. $date1',
nn='ca. $date1',
nds='üm un bi $date1',
nl='ca. $date1',
no='ca. $date1',
pl={'około $date1', case={'gen',''}},
pt='cerca de $date1',
qu='$date1chá',
ro='circa $date1',
ru={'около $date1', case={'gen',''}},
sk='cca $date1',
sl={function(date1, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='c:a $date1', -- template:Circa used 'ca $date1'
uk={'близько $date1', case={'gen', ''}},
ur='قریباً $date1',
vi='vào khoảng $date1',
['zh-hans']='约$date1',
['zh-hant']='約$date1',
default='<span style="color:red">circa $date1</span>',
},
['uncertain'] = {
nDates = 1,
['zh-hans']='$date1(?)',
['zh-hant']='$date1(?)',
default='$date1 (?)',
},
probably = { -- see Q56644435
nDates = 1,
ast = 'probablemente $date1',
bn = 'সম্ভবত $date1',
ca = 'probablement $date1',
cs = 'pravděpodobně $date1',
de = 'wahrscheinlich $date1',
el = 'πιθανόν $date1',
en = 'probably $date1',
['en-gb']='probably $date1',
['en-ca']='probably $date1',
eo = 'probable $date1',
es = 'probable $date1',
fa = 'احتمالاً $date1',
fr = 'probablement $date1',
he = 'כפי הנראה $date1',
hu = 'feltehetőleg $date1',
it = 'probabile $date1',
mk = 'веројатно $date1',
ml = 'ഒരുപക്ഷേ $date1',
nl = 'waarschijnlijk $date1',
pl = 'prawdopodobnie $date1',
pt = 'provavelmente $date1',
ru = 'вероятно $date1',
sv = 'antagligen $date1',
uk = 'ймовірно $date1',
default='<span style="color:red">probably $date1</span>',
},
possibly = { -- see Q30230067
nDates = 1,
ast = 'posiblemente $date1',
bn = 'যথাসম্ভব $date1',
cs = 'možná $date1',
de = 'möglicherweise $date1',
en = 'possibly $date1',
['en-gb']='possibly $date1',
['en-ca']='possibly $date1',
eo = 'eble $date1',
el = 'πιθανόν $date1',
fa = 'احتمالاً $date1',
fr = 'peut-être $date1',
hu = 'talán $date1',
mk = 'можеби $date1',
ml = 'മിക്കവാറും $date1',
nl = 'mogelijk $date1',
pl = 'być może $date1',
pt = 'possivelmente $date1',
ro = 'poate $date1',
ru = 'возможно $date1',
sv = 'möjligen $date1',
uk = 'можливо $date1',
default='<span style="color:red">possibly $date1</span>',
},
presumably = { -- see Q18122778
nDates = 1,
ast = 'presumiblemente $date1',
de = 'vermutlich $date1',
en = 'presumably $date1',
['en-gb']='presumably $date1',
['en-ca']='presumably $date1',
fr = 'présumé $date1',
it = 'presumibilmente $date1',
ml = 'ഊഹപ്രകാരം $date1',
ru = 'предположительно $date1',
pl = 'przypuszczalnie $date1',
uk = 'здогадно $date1',
default='<span style="color:red">presumably $date1</span>',
},
['year unknown']= {
nDates = 0,
ca='Any desconegut',
['be-tarask']='Год невядомы',
bn='অজানা বছর',
cs='neznámý rok',
de='Jahr unbekannt',
el='άγνωστο έτος',
en='year unknown',
['en-gb']='year unknown',
['en-ca']='year unknown',
eo='jaro nesciata',
es='Año desconocido',
fa='سال نامعلوم',
fi='tuntematon vuosi',
fr='Année inconnue',
he='שנה לא ידועה',
hsb='lěto njeznate',
hr='nepoznata godina',
it='anno sconosciuto',
ja='年代不明',
la='Sine anno',
ml='വർഷം ലഭ്യമല്ല',
mk='непозната година',
nb='ukjent år',
nn='ukjent år',
nl='zonder jaar',
no='ukjent år',
pl='rok nieznany',
qu='Mana riqsisqa wata',
ru='Год неизвестен',
sk='neznámy rok',
sl='neznano leto',
sv='Okänt år',
uk='рік невідомий',
ur='سال نامعلوم',
vi='không rõ năm',
['zh-hans']='年份未知',
['zh-hant']='年份未知',
default='<span style="color:red">year unknown</span>',
},
-- ==========================================
-- === Parts of a whole =====================
-- ==========================================
['firsthalf'] = {
nDates = 1,
ar='النصف الأول من $date1',
['be-tarask']={'першая палова $date1', case={'gen',''}},
bn='$date1-এর প্রথমার্ধ',
ca='1a meitat de $date1',
cs={'1. polovina $date1', case={'gen',''}},
da='1. halvdel af $date1',
de='1. Hälfte $date1',
el='πρώτο μισό του $date1',
en='first half of $date1',
['en-gb']='first half of $date1',
['en-ca']='first half of $date1',
eo='unua duono de $date1',
es='primera mitad de $date1',
et='$date1 esimene pool',
fa='نیمهٔ نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alkupuolisko' end, case={'gen',''}},
fr='1<sup>re</sup> moitié de $date1',
gl='primeira metade de $date1',
he='במחצית הראשונה של $date1',
hsb={'prěnja połojca $date1', case={'gen',''}},
hu='$date1 első felében',
hr='prva polovina $date1',
it='prima metà del $date1',
ja='$date1上半期',
mk='прва половина на $date1',
ml='$date1 ആദ്യപകുതി',
nb='første halvdel av $date1',
nn='fyrste halvdelen av $date1',
nds='1. Helft $date1',
nl='eerste helft $date1',
no='første halvdel av $date1',
pl={'pierwsza połowa $date1', case={'gen',''}},
pt='primeira metade de $date1',
qu='$date1 ñawpaq kuskanpi',
ro='prima jumătate a lui $date1',
ru={'первая половина $date1', case={'gen',''}},
sk={'1. polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'prva polovica ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första halvan av $date1',
uk={'перша половина $date1', case={'gen',''}},
ur='نصف اول $date1',
vi='nửa đầu năm $date1',
['zh-hans']='$date1上半期',
['zh-hant']='$date1上半期',
default='<span style="color:red">first half of $date1</span>',
},
['secondhalf'] = {
nDates = 1,
ar='النصف الثاني من $date1',
['be-tarask']={'другая палова $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয়ার্ধ',
ca='2a meitat de $date1',
cs={'2. polovina $date1', case={'gen',''}},
da='2. halvdel af $date1',
de='2. Hälfte $date1',
el='δεύτερο μισό του $date1',
en='second half of $date1',
['en-gb']='second half of $date1',
['en-ca']='second half of $date1',
eo='dua duono de $date1',
es='segunda mitad de $date1',
et='$date1 teine pool',
fa='نیمهٔ دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppupuolisko' end, case={'gen',''}},
fr='2<sup>e</sup> moitié de $date1',
gl='segunda metade de $date1',
he='במחצית השנייה של $date1',
hsb={'druha połojca $date1', case={'gen',''}},
hr='druga polovina $date1',
hu='$date1 második felében',
it='seconda metà del $date1',
ja='$date1下半期',
mk='втора половина на $date1',
ml='$date1 രണ്ടാം പകുതി',
nb='andre halvdel av $date1',
nn='andre halvdelen av $date1',
nds='2. Helft vun $date1',
nl='tweede helft $date1',
no='andre halvdel av $date1',
pl={'druga połowa $date1', case={'gen',''}},
pt='segunda metade de $date1',
qu='$date1 qhipaq kuskanpi',
ro='a doua jumătate a lui $date1',
ru={'вторая половина $date1', case={'gen',''}},
sk={'2. polovica $date1', case={'gen',''}},
sl={'druga polovica {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sv='andra halvan av $date1',
uk={'друга половина $date1', case={'gen',''}},
ur='نصف آخر $date1',
vi='nửa cuối năm $date1',
['zh-hans']='$date1下半期',
['zh-hant']='$date1下半期',
default='<span style="color:red">second half of $date1</span>',
},
['1quarter']= {
nDates = 1,
bn='$date1-এর প্রথম ত্রৈমাসিক',
['be-tarask']={'першая чвэрць $date1', case={'gen',''}},
cs={'1. čtvrtina $date1', case={'gen',''}},
de='erstes Viertel $date1',
en='first quarter of $date1',
['en-gb']='first quarter of $date1',
['en-ca']='first quarter of $date1',
eo='unua kvarono de $date1',
es='primer cuarto del $date1',
fa='ربع نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 ensimmäinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'premier quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון ראשון של $date1',
hsb={'prěnja štwórćina $date1', case={'gen',''}},
hr='prva četvrtina $date1',
it='primo quarto del $date1',
ja='$date1第1四半期',
ml='$date1 ഒന്നാം ത്രൈമാസം',
nb='første kvartal av $date1',
nl='eerste kwart $date1',
nn='fyrste kvartalet av $date1',
no='første kvartal av $date1',
pl={'pierwsza ćwiartka $date1', case={'gen',''}},
qu='$date1 ñawpaq ñiqin tawa phatmanpi',
ru={'первая четверть $date1', case={'gen',''}},
sk={'1. štvrtina $date1', case={'gen',''}},
sl={'prva četrtina {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sl={function(date1, state) return 'prva četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första kvartalet av $date1',
uk={'1-ша чверть $date1', case={'gen',''}},
ur='پہلی سہ ماہی $date1',
vi='quý 1 năm $date1',
['zh-hans']='$date1第一季度',
['zh-hant']='$date1第一季度',
default='<span style="color:red">first quarter of $date1</span>',
},
['2quarter']= {
nDates = 1,
['be-tarask']={'другая чвэрць $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয় ত্রৈমাসিক',
cs={'2. čtvrtina $date1', case={'gen',''}},
de='zweites Viertel $date1',
en='second quarter of $date1',
['en-gb']='second quarter of $date1',
['en-ca']='second quarter of $date1',
eo='dua kvarono de $date1',
es='segundo cuarto del $date1',
fa='ربع دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 toinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'deuxième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שני של $date1',
hsb={'druha štwórćina $date1', case={'gen',''}},
hr='druga četvrtina $date1',
it='secondo quarto del $date1',
ja='$date1第2四半期',
ml='$date1 രണ്ടാം ത്രൈമാസം',
nb='andre kvartal av $date1',
nl='tweede kwart $date1',
nn='andre kvartalet av $date1',
no='andre kvartal av $date1',
pl={'druga ćwiartka $date1', case={'gen',''}},
qu='$date1 iskay ñiqin tawa phatmanpi',
ru={'вторая четверть $date1', case={'gen',''}},
sk={'2. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'druga četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='andra kvartalet av $date1',
uk={'2-га чверть $date1', case={'gen',''}},
ur='دوسری سہ ماہی $date1',
vi='quý 2 năm $date1',
['zh-hans']='$date1第二季度',
['zh-hant']='$date1第二季度',
default='<span style="color:red">second quarter of $date1</span>',
},
['3quarter']= {
nDates = 1,
['be-tarask']={'трэцяя чвэрць $date1', case={'gen',''}},
bn='$date1-এর তৃতীয় ত্রৈমাসিক',
cs={'3. čtvrtina $date1', case={'gen',''}},
de='drittes Viertel $date1',
en='third quarter of $date1',
['en-gb']='third quarter of $date1',
['en-ca']='third quarter of $date1',
eo='tria kvarono de $date1',
es='tercer cuarto del $date1',
fa='ربع سوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 kolmas neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'troisième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שלישי של $date1',
hsb={'třeća štwórćina $date1', case={'gen',''}},
hr='treća četvrtina $date1',
it='terzo quarto del $date1',
ja='$date1第3四半期',
ml='$date1 മൂന്നാം ത്രൈമാസം',
nb='tredje kvartal av $date1',
nl='derde kwart $date1',
nn='tredje kvartalet av $date1',
no='tredje kvartal av $date1',
pl={'trzecia ćwiartka $date1', case={'gen',''}},
qu='$date1 kimsa ñiqin tawa phatmanpi',
sk={'3. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'tretja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
ru={'третья четверть $date1', case={'gen',''}},
sv='tredje kvartalet av $date1',
uk={'3-тя чверть $date1', case={'gen',''}},
ur='تیسری سہ ماہی $date1',
vi='quý 3 năm $date1',
['zh-hans']='$date1第三季度',
['zh-hant']='$date1第三季度',
default='<span style="color:red">third quarter of $date1</span>',
},
['4quarter']= {
nDates = 1,
['be-tarask']={'чацьвёртая чвэрць $date1', case={'gen',''}},
bn='$date1-এর চতুর্থ ত্রৈমাসিক',
cs={'4. čtvrtina $date1', case={'gen',''}},
de='letztes Viertel $date1',
en='fourth quarter of $date1',
['en-gb']='fourth quarter of $date1',
['en-ca']='fourth quarter of $date1',
eo='kvara kvarono de $date1',
es='último cuarto del $date1',
fa='ربع چهارم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 viimeinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'quatrième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון רביעי של $date1',
hsb={'poslednja štwórćina $date1', case={'gen',''}},
hr='četvrta četvrtina $date1',
it='quarto quarto del $date1',
ja='$date1第4四半期',
ml='$date1 നാലാം ത്രൈമാസം',
nb='fjerde kvartal av $date1',
nl='vierde kwart $date1',
nn='tredje kvartalet av $date1',
no='fjerde kvartal av $date1',
pl={'czwarta ćwiartka $date1', case={'gen',''}},
qu='$date1 qhipaq ñiqin tawa phatmanpi',
ru={'последняя четверть $date1', case={'gen',''}},
sk={'4. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'zadnja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='fjärde kvartalet av $date1',
uk={'4-та чверть $date1', case={'gen',''}},
ur='چوتھی سہ ماہی $date1',
vi='quý 4 năm $date1',
['zh-hans']='$date1第四季度',
['zh-hant']='$date1第四季度',
default='<span style="color:red">forth quarter of $date1</span>',
},
-- ==========================================
-- === Seasons ==============================
-- ==========================================
['spring'] = {
nDates = 1,
ar='ربيع $date1',
['be-tarask']='вясна $date1',
bn='বসন্তকাল $date1',
ca='primavera $date1',
cs={'jaro $date1', case={'gen', ''}},
da='Forår $date1',
de='Frühling $date1',
en='Spring $date1',
['en-gb']='Spring $date1',
['en-ca']='Spring $date1',
eo='printempo $date1',
es='primavera de $date1',
et='$date1 kevad',
fa='بهار $date1',
fi='Kevät $date1',
fr='printemps $date1',
gl='primavera $date1',
he='אביב $date1',
hr='proljeće $date1',
hsb='nalěćo $date1',
hu='$date1 tavasza',
it='primavera $date1',
ja='{{#if:$date1|$date1年}}春',
mk='пролет $date1',
ml='$date1 വസന്തം',
nb='våren $date1',
nds='Vörjohr $date1',
nn='våren $date1',
nl='voorjaar $date1',
no='våren $date1',
pl='wiosna $date1',
pt='primavera de $date1',
ro='primăvara lui $date1',
ru='весна $date1',
sk={'jar $date1', case={'gen', ''}},
sl='pomlad $date1',
sv='våren $date1',
uk='весна $date1',
ur='بہار $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}春季',
['zh-hant']='{{#if:$date1|$date1年}}春季',
default='<span style="color:red">Spring $date1</span>',
},
['summer'] = {
nDates = 1,
ar='صيف $date1',
['be-tarask']='лета $date1',
bn='গ্রীষ্মকাল $date1',
ca='estiu $date1',
cs={'léto $date1', case={'gen', ''}},
da='Sommer $date1',
de='Sommer $date1',
en='Summer $date1',
['en-gb']='Summer $date1',
['en-ca']='Summer $date1',
eo='somero $date1',
es='verano de $date1',
et='$date1 suvi',
fa='تابستان $date1',
fi='Kesä $date1',
fr='été $date1',
gl='verán $date1',
he='קיץ $date1',
hr='ljeto $date1',
hsb='lěćo $date1',
hu='$date1 nyara',
it='estate $date1',
ja='{{#if:$date1|$date1年}}夏',
mk='лето $date1',
ml='$date1 വേനൽ',
nb='sommeren $date1',
nds='Sommer $date1',
nn='sumaren $date1',
nl='zomer $date1',
no='sommeren $date1',
pl='lato $date1',
pt='verão de $date1',
ro='vara lui $date1',
ru='лето $date1',
sk={'leto $date1', case={'gen', ''}},
sl='poletje $date1',
sv='sommaren $date1',
uk='літо $date1',
ur='گرما $date1',
vi='mùa hè năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}夏季',
['zh-hant']='{{#if:$date1|$date1年}}夏季',
default='<span style="color:red">Summer $date1</span>',
},
['autumn'] = {
nDates = 1,
ar='خريف $date1',
['be-tarask']='восень $date1',
bn='শরত্কাল $date1',
ca='tardor $date1',
cs={'podzim $date1', case={'gen', ''}},
da='Efterår $date1',
de='Herbst $date1',
en='Autumn $date1',
['en-gb']='Autumn $date1',
['en-ca']='Autumn $date1',
eo='aŭtuno $date1',
es='otoño de $date1',
et='$date1 sügis',
fa='پاییز $date1',
fi='Syksy $date1',
fr='automne $date1',
gl='outono $date1',
he='סתיו $date1',
hr='jesen $date1',
hsb='nazyma $date1',
hu='$date1 ősze',
it='autunno $date1',
ja='{{#if:$date1|$date1年}}秋',
mk='есен $date1',
ml='$date1 ശരത്കാലം',
nb='høsten $date1',
nds='Harvst $date1',
nn='hausten $date1',
nl='herfst $date1',
no='høsten $date1',
pl='jesień $date1',
pt='outono de $date1',
ro='toamna lui $date1',
ru='осень $date1',
sk={'jeseň $date1', case={'gen', ''}},
sl='jesen $date1',
sv='hösten $date1',
uk='осінь $date1',
ur='خزاں $date1',
vi='mùa thu năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}秋季',
['zh-hant']='{{#if:$date1|$date1年}}秋季',
default='<span style="color:red">Autumn $date1</span>',
},
['winter'] = {
nDates = 1,
ar='شتاء $date1',
['be-tarask']='зіма $date1',
bn='শীতকাল $date1',
ca='hivern $date1',
cs={'zima $date1', case={'gen', ''}},
da='Vinter $date1',
de='Winter $date1',
en='Winter $date1',
['en-gb']='Winter $date1',
['en-ca']='Winter $date1',
eo='vintro $date1',
es='invierno de $date1',
et='$date1 talv',
fa='زمستان $date1',
fi='Talvi $date1',
fr='hiver $date1',
gl='inverno $date1',
he='חורף $date1',
hr='zima $date1',
hsb='zyma $date1',
hu='$date1 tele',
it='inverno $date1',
ja='{{#if:$date1|$date1年}}冬',
mk='зима $date1',
ml='$date1 ശൈത്യകാലം',
nds='Winter $date1',
nl='winter $date1',
nb='vinteren $date1',
nn='vinteren $date1',
no='vinteren $date1',
pl='zima $date1',
pt='inverno de $date1',
ro='iarna lui $date1',
ru='зима $date1',
sk={'zima $date1', case={'gen', ''}},
sl='zima $date1',
sv='vintern $date1',
uk='зима $date1',
ur='سرما $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}冬季',
['zh-hant']='{{#if:$date1|$date1年}}冬季',
default='<span style="color:red">Winter $date1</span>',
},
-- ==========================================
-- === Decade/Century/Millennium ============
-- ==========================================
['decade'] = {
nDates = 1,
ar='أعوام $date1',
['be-tarask']='$date1-я гады',
bn='$date1-এর দশক',
ca='dècada del $date1',
cs=function(date1, state) return ((date1 % 100 == 0) and 'první dekáda' or ((date1 % 100) .. '. léta')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. století' end,
da="$date1'erne",
de='$date1er-Jahre',
el='δεκαετία του $date1',
en='$date1s',
['en-gb']='$date1s',
['en-ca']='$date1s',
eo='$date1-aj jaroj',
es='años $date1',
et='$date1ndad',
fa='دههٔ $date1',
fi='$date1-luvulla',
fr='années $date1',
gl='anos $date1',
he='שנות ה-$date1',
hr='$date1-ih',
hsb='$date1te lěta',
hu='$date1-s évek',
it="{{#ifexpr: $date1 mod 100 = 0 | primo decennio del {{Roman|{{#expr: ($date1 / 100) + 1}}}} sec. | anni '{{#expr: $date1 mod 100 }} del {{Roman|{{#expr: floor($date1 / 100) + 1}}}} sec.}}",
ja='$date1年代',
mk='$date1-ти',
ml='ദശകം $date1-ൽ',
nb='$date1-årene',
nn='$date1-åra',
nds='Johren $date1',
nl='jaren $date1',
no='$date1-årene',
pl=function(date1, state)
local str = 'lata '..(date1 % 100)..'. '
if (date1 % 100 == 0) then
str = 'pierwsza dekada '
elseif (date1 % 100 == 10) then
str = 'druga dekada '
end
return str .. Roman(math.floor(date1 / 100) + 1) .. ' wieku'
end,
pt='década de $date1',
qu='$date1 watakuna',
ro='anii $date1',
ru='$date1-е годы',
sk=function(date1, state) return ((date1 % 100 == 0) and 'prvé desaťročie' or ((date1 % 100) .. '. roky')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. storočia' end,
sl='$date1-ta leta',
sv='$date1-talet{{#switch:$date1|1300|1400|1500|1600|1700|1800|1900|2000|2100= (decennium)}}',
uk='$date1-ті роки',
ur='دہائی $date1',
vi='thập niên $date1',
['zh-hans']='$date1年代',
['zh-hant']='$date1年代',
default='<span style="color:red">$date1s</span>',
},
['century'] = {
nDates = 1,
ar='القرن $date1',
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' стагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' শতাব্দী' end,
ca='segle {{small-caps|{{lc:{{Roman year|$date1}}}}}}',
cs='$date1. století',
da='$date1. århundrede',
de='$date1. Jahrhundert',
el='$date1ος αιώνας',
en=function(date1, state) return Ordinal(date1, 'en') .. ' century' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' century' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' century' end,
eo='$date1-a jarcento',
es=function(date1, state) return 'siglo ' .. Roman(date1) end,
et='$date1. sajand',
fa='سدهٔ $date1',
fi='{{#expr: ($date1 - 1) * 100}}-luvulla',
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle' end,
gl=function(date1, state) return 'século ' .. Roman(date1) end,
he='המאה ה-$date1',
hr='$date1. stoljeće',
hsb='$date1. lětstotk',
hu='$date1. évszázad',
it=function(date1, state) return Roman(date1) .. ' sec.' end,
ja='$date1世紀',
la='saeculum $date1',
mk=function(date1, state) return Roman(date1) .. ' век' end,
ml='$date1-ആം നൂറ്റാണ്ട്',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' århundre' end,
nn=function(date1, state) return 'det ' .. Ordinal(date1, 'no') .. ' århundre' end,
nds='$date1. Johrhunnert',
nl='$date1e eeuw',
no=function(date1, state) return Ordinal(date1, 'no') .. ' århundre' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decCentury('pl',state.case) end,
pt=function(date1, state) return 'século ' .. Roman(date1) end,
qu='$date1 ñiqin pachakwata',
ro='secolul al $date1-lea',
ru=function(date1, state) return Roman(date1) .. ' век' end,
sk='$date1. storočie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. stoletje' end,
sv='{{#expr:$date1 - 1}}00-talet',
tt=function(date1, state) return Roman(date1) .. ' гасыр' end,
uk=function(date1, state) return Roman(date1) .. ' ст.' end,
ur='صدی $date1',
vi='thế kỷ $date1',
['zh-hans']='$date1世纪',
['zh-hant']='$date1世紀',
default='<span style="color:red">$date1 century</span>',
},
['millennium'] = {
nDates = 1,
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' тысячагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' সহস্রাব্দ' end,
cs='$date1. tisíciletí',
de='$date1. Jahrtausend',
en=function(date1, state) return Ordinal(date1, 'en') .. ' millennium' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' millennium' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' millennium' end,
eo='$date1-a jarmilo',
es=function(date1, state) return Roman(date1) .. ' milenio' end,
fa=function(date1, state) return 'هزارهٔ ' .. Ordinal(date1, 'fa') end,
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' millénaire' end,
hr='$date1. tisućljeće',
hsb='$date1. lěttysac',
it=function(date1, state) return Roman(date1) .. ' millennio' end,
ja='$date1千年紀',
la='millennium $date1',
ml='$date1-മത്തെ സഹസ്രാബ്ദം',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' årtusen' end,
nn=function(date1, state) return Ordinal(date1, 'no') .. ' tusenår' end,
nl='$date1e millennium',
no=function(date1, state) return Ordinal(date1, 'no') .. ' årtusen' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decMillennium('pl',state.case) end,
pt=function(date1, state) return Roman(date1) .. ' milénio' end,
qu='$date1 ñiqin waranqawata',
ru=function(date1, state) return Roman(date1) .. ' тысячелетие' end,
sk='$date1. tisícročie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. tisočletje' end,
sv=function(date1, state) return Ordinal(date1, 'sv') .. ' årtusendet' end,
uk=function(date1, state) return Roman(date1) .. ' тис.' end,
ur=function(date1, state) return 'ہزارہ ' .. Ordinal(date1, 'ur') end,
vi='thiên niên kỷ $date1',
['zh-hans']=function(date1, state) return Ordinal(date1, 'zh-hans') .. '千年' end,
['zh-hant']=function(date1, state) return Ordinal(date1, 'zh-hant') .. '千年' end,
--default='{{Please translate|$date1 millennium|Module:I18n/complex date}}',
default='<span style="color:red">$date1 millennium</span>',
},
['million'] = {
de = '$date1 Millionen Jahre',
en = '$date1 million years',
['en-gb'] = '$date1 million years',
['en-ca'] = '$date1 million years',
eo = '$date1 milionoj da jaroj',
es = '$date1 millones de años',
nl = '$date1e miljoen jaar',
fr = '$date1 millions d’années',
it = '$date1 milioni di anni',
ml = function(date1, state)
if date1 >= 10 then
return (date1 / 10) .. ' കോടി വർഷങ്ങൾ' -- 100 lakh = 1 crore
else
return (date1 * 10) .. ' ലക്ഷം വർഷങ്ങൾ'
end
end,
nb = '$date1 millioner år',
nl = '$date1 miljoen jaar',
nn = '$date1 millionar år',
pl = '$date1 milionów lat',
pt = '$date1 milhões de anos',
ru = '$date1 миллиона лет',
uk = '$date1 мільйони років',
ur = '$date1 ملین سال',
['zh-hans'] = '$date1百万年',
['zh-hant'] = '$date1百万年',
default='<span style="color:red">$date1 million years</span>',
nDates = 1,
},
['billion'] = {
de = '$date1 Milliarden Jahre',
en = '$date1 billion years',
['en-gb'] = '$date1 billion years',
['en-ca'] = '$date1 billion years',
es = '$date1 mil millones de años',
nl = '$date1e miljard jaar',
fr = '$date1 milliard d’années',
it = '$date1 miliardi di anni',
ml = function(date1, state) return (date1 * 10) .. ' കോടി വർഷങ്ങൾ' end,
nb = '$date1 milliarder år',
nl = '$date1 miljard jaar',
nn = '$date1 milliardar år',
pl = '$date1 miliardów lat',
pt = '$date1 mil milhões de anos',
ru = '$date1 миллиардов лет',
uk = '$date1 мільярдів років',
ur = '$date1 بلین سال',
['zh-hans'] = '$date1十亿年',
['zh-hant'] = '$date1十億年',
default='<span style="color:red$date1 billion years</span>',
nDates = 1,
},
-- ==========================================
-- === Complex phrases ======================
-- ==========================================
-- Phrases which for some languages can be build out of simpler phrases
-- Their "default" state, used when no translation in the requested language was found, returns a "state" like
-- table which is used to call the function again and built the phrase out of simple phrases
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the century'] = {
nDates = 2,
['be-tarask']=function(date1, date2, state) return 'канец ' .. Roman(date1) ..' — пачатак ' .. Roman(date2) ..' стагодзьдзя' end,
bn=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'bn') .. ' শতাব্দীর পালাবদলে' end,
cs='přelom $date1. a $date2. století',
de=function(date1, date2, state) return 'Jahrhundertwende $date1/' .. Ordinal(date2, 'de') end,
en=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en') .. ' century' end,
['en-gb']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-gb') .. ' century' end,
['en-ca']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-ca') .. ' century' end,
eo='ŝanĝo de la $date1-a al la $date2-a jarcentoj',
es=function(date1, date2, state) return 'hacia finales del siglo ' .. Roman(date1) .. ' y principios del siglo ' .. Roman(date2) .. '' end,
fi='{{#expr: $date1 * 100-100}}-luvun loppu ja {{#expr: $date2 * 100-100}}-luvun alku',
fr=function(date1, date2, state) return 'fin du ' .. Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle / début du ' .. Ordinal(date2, 'fr', 'roman', 'm') end,
hr='kraj $date1. i početak $date2. stoljeća',
hsb=function(date1, date2, state) return 'přechod ' .. Roman(date1) ..' do ' .. Roman(date2) ..' lětstotka' end,
it=function(date1, date2, state) return 'fine del ' .. Roman(date1) .. ' sec. e inizio del ' .. Roman(date2) .. ' sec.' end,
ja='$date1世紀から$date2世紀への変わり目',
ml='$date1 നൂറ്റാണ്ട് അവസാനവും $date2 നൂറ്റാണ്ട് ആദ്യവും',
nb=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nb') .. ' århundre/begynnelsen av det ' .. Ordinal(date2, 'nb') .. ' århundre' end,
nl=function(date1, date2, state) return 'eind $date1e/begin $date2e eeuw' end,
nn=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nn') .. ' hundreåret/byrjinga av det ' .. Ordinal(date2, 'nn') .. ' hundreåret' end,
pl=function(date1, date2, state) return 'przełom ' .. Roman(date1) ..' i ' .. Roman(date2) ..' wieku' end,
qu="$date1 ñiqin waranqawatamanta $date2 ñiqin waranqawataman t'ikrasqanpi",
ru=function(date1, date2, state) return 'конец ' .. Roman(date1) ..' — начало ' .. Roman(date2) ..' века' end,
sk='prelom $date1. a $date2. storočia',
sl=function(date1, date2, state) return 'pozno ' .. Ordinal(date1, 'sl') .. '. in zgodnje ' .. Ordinal(date2, 'sl') .. '. stoletje' end,
sv=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'sv') .. ' årtusendets sekelskifte' end,
uk=function(date1, date2, state) return 'кін. ' .. Roman(date1) ..' ст. — поч. ' .. Roman(date2) ..' ст.' end,
vi='bước sang thế kỷ $date2',
['zh-hans']=function(date1, date2, state) return '$date1世纪末' .. Ordinal(date2, 'zh-hans') .. '世纪初' end,
['zh-hant']=function(date1, date2, state) return '$date1世紀末' .. Ordinal(date2, 'zh-hant') .. '世紀初' end,
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='century', era1=state.era1, adj2 = 'early', unit2='century', era2=state.era2} end,
},
-- $date1 - original date - 10
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the decade']={
nDates = 2,
['be-tarask']='канец $date1-х — пачатак $date2-х гг.',
bn='$date1-এর দশকের শেষে এবং $date2-এর দশকের শুরুতে',
cs=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "přelom 1. a 2. dekády " .. century1 .. ". století"
elseif decade2 == 0 and date2 < date1 then
return "přelom 9. a 10. dekády " .. century1 .. ". století"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "přelom " .. century1 .. ". a " .. century2 .. ". století"
else
return "přelom " .. decade1 .. "0. a " .. decade2 .. "0. let " .. century1 .. ". století"
end
end,
de='späte $date1er und frühe $date2er',
en='late $date1s and early $date2s',
['en-gb']='late $date1s and early $date2s',
['en-ca']='late $date1s and early $date2s',
eo='ŝanĝo de la $date1-a al la $date2-a jardekoj',
es='finales de los años $date1 y principios de los años $date2',
fa='اواخر دههٔ $date1 و اوایل دههٔ $date2',
fi='$date1-luvun loppu ja $date2-luvun alku',
fr='fin des années $date1 / début des années $date2',
hr='kraj $date1-ih i početak $date2-ih',
hsb='pózdnje $date1te a zažne $date2te lěta',
it='fine degli anni $date1 / inizio degli anni $date2',
ja='$date1年代末から$date2年代初頭',
ml='$date1കൾ അവസാനവും $date2കൾ ആദ്യവും',
nb = 'sent på $date1-tallet og tidlig på $date2-tallet',
nl = 'eind $date1e/begin $date2e decennium',
nn = 'seint på $date1-talet og tidleg på $date2-talet',
pl=function(date1, date2, state)
local decade1 = math.floor(date1 % 100)
local decade2 = math.floor(date2 % 100)
local century1 = Roman(math.floor( date1 / 100) + 1)
local century2 = Roman(math.floor( date2 / 100) + 1)
if decade1 == 0 and date1 < date2 then
return "przełom pierwszej i drugiej dekady " .. century1 .. " wieku"
elseif decade1 == 10 and date1 < date2 then
return "przełom drugiej dekady i lat 20. " .. century1 .. " wieku"
elseif decade1 == 90 and decade2 == 0 then
return "przełom ostatniej dekady " .. century1 .. " i pierwszej dekady " .. century2 .. " wieku"
else
return "przełom lat " .. decade1 .. ". i " .. decade2 .. ". " .. century1 .. " wieku"
end
end,
qu='qhipaq $date1 watakuna ñawpaq $date2 watakunapas',
ru='конец $date1-х — начало $date2-х гг.',
sv='sena $date1-talet och tidiga $date2-talet',
sk=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "prelom 1. a 2. desaťročia " .. century1 .. ". storočia"
elseif decade2 == 0 and date2 < date1 then
return "prelom 9. a 10. desaťročia " .. century1 .. ". storočia"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "prelom " .. century1 .. ". a " .. century2 .. ". storočia"
else
return "prelom " .. decade1 .. "0. a " .. decade2 .. "0. rokov " .. century1 .. ". storočia"
end
end,
sl='pozna $date1-ta in zgodnja $date2-ta leta',
uk='кінець $date1-х — початок $date2-х рр.',
ur='اخیر دہائی $date1 و ابتدا دہائی $date2',
vi='bước sang thập niên $date2',
['zh-hans']='$date1年代末$date2年代初',
['zh-hant']='$date1年代末$date2年代初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='decade', era1=state.era1, adj2 = 'early', unit2='decade', era2=state.era2} end,
},
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the year']={
nDates = 2,
['be-tarask']='канец $date1 — пачатак $date2 г.',
bn='$date1-এর শেষে এবং $date2-এর শুরুতে',
cs='přelom roku $date1/$date2',
de='Jahreswechsel $date1/$date2',
en='late $date1 and early $date2',
['en-gb']='late $date1 and early $date2',
['en-ca']='late $date1 and early $date2',
eo='jarŝanĝo $date1 / $date2',
es='finales de $date1 y principios de $date2',
fa='اواخر $date1 و اوایل $date2',
fi='vuoden $date1 loppu ja vuoden $date2 alku',
fr='fin $date1 / début $date2',
hsb='kónc $date1/spočatk $date2',
ja='$date1年末から$date2年初め',
ml='$date1 അവസാനവും $date2 ആദ്യവും',
nb='årsskiftet $date1/$date2',
nl='eind $date1 en begin $date2',
nn='årsskiftet $date1/$date2',
pl='koniec $date1 i początek $date2 roku',
qu="$date1 qhipaq p'unchawkuna $date2 ñawpaq p'unchawkunapas",
ru='конец $date1 — начало $date2 г.',
sk='prelom rokov $date1/$date2',
sl='konec leta $date1 in začetek $date2',
sv='sena $date1 och tidiga $date2',
uk='кінець $date1 — початок $date2 р.',
ur='اخیر $date1 و ابتدا $date2',
vi='bước sang năm $date2',
['zh-hans']='$date1年末$date2年初',
['zh-hant']='$date1年末$date2年初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='year', era1=state.era1, adj2 = 'early', unit2='year', era2=state.era2} end,
},
['circa2'] = {
nDates = 2,
ar='حوالي $date1 – $date2',
['be-tarask']={'прыкладна з $date1 да прыкладна $date2', case={'gen','gen'}},
bn='প্রায় $date1 এবং প্রায় $date2-এর মাঝামাঝি',
ca='cap a $date1 o $date2',
cs='cca $date1 až $date2',
da='ca. mellem $date1 og $date2',
de='etwa $date1 bis $date2',
el='περίπου μεταξύ $date1 και $date2',
en='between circa $date1 and circa $date2',
['en-gb']='between circa $date1 and circa $date2',
['en-ca']='between circa $date1 and circa $date2',
eo='proksimume de $date1 ĝis $date2',
es='hacia $date1 o $date2',
et='ca. $date1 ja $date2 vahel',
fa='حدوداً بین $date1 و $date2',
fi={function(date1, date2, state) return 'suunnilleen ' .. decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='vers $date1-$date2',
gl='entre $date1 e $date2',
he='בסביבות ה- $date1 עד $date2',
hr='oko između $date1 i $date2',
hsb={'wot něhdźe $date1 do něhdźe $date2', case={'gen','gen'}},
hu='$date1 és $date2 között',
it='tra il $date1 e il $date2 circa',
ja='$date1頃と$date2頃の間',
mk='~ помеѓу $date1 и $date2',
ml='ഉദ്ദേശം $date1-നും ഉദ്ദേശം $date2-നും ഇടയ്ക്ക്',
nb='ca. mellom $date1 og $date2',
nn='ca. mellom $date1 og $date2',
nds='üm un bi $date1 bet $date2',
nl='ca. $date1 tot $date2',
no='ca. mellom $date1 og $date2',
pl={'od około $date1 do około $date2', case={'gen','gen'}},
pt='cerca de $date1 a $date2',
qu="$date1 p'unchawmantachá $date2 p'unchawkamachá",
ro='circa $date1 – $date2',
ru={'с около $date1 до, примерно, $date2', case={'gen','gen'}},
sk='cca $date1 až $date2',
sl={function(date1, date2, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1 do $date2' end, case={'gen','gen'}},
sv='c:a $date1 till $date2',
uk={'з близько $date1 до, приблизно, $date2', case={'gen','gen'}},
ur='قریباً بین $date1 و $date2',
vi='giữa vào khoảng $date1 và vào khoảng $date2',
['zh-hans']='约$date1~$date2间',
['zh-hant']='約$date1~$date2間',
default=function(date1, date2, state) return {conj='between', adj1 = 'circa', unit1=state.unit1, era1=state.era1, adj2 = 'circa', unit2=state.unit2, era2=state.era2} end,
},
-- ==========================================
-- === Eras and calendars ===================
-- ==========================================
['julian'] = {
nDates = 2,
ar='$date1 ($date2 حسب [[w:ar:التقويم اليولياني|تقويم يولياني]])',
['be-tarask']='$date1 ($date2 паводле [[w:be-tarask:Юліянскі каляндар|ст. ст.]]',
bn='$date1 (জুলিয়ান ক্যালেন্ডারে $date2)',
ca='$date1 ($date2 segons el [[w:ca:Calendari julià|calendari julià]])',
cs='$date1 ($date2 podle [[w:cs:Juliánský kalendář|juliánského kalendáře]])',
da='$date1 ($date2 [[w:da:Julianske kalender|Julianske kalender]])',
de='$date1 ($date2 [[w:de:Julianischer Kalender|Julianischer Kalender]])',
el='$date1 ($date2 με το [[w:el:Ιουλιανό ημερολόγιο|Ιουλιανό ημερολόγιο]])',
en='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-gb']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-ca']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
fa='$date1 ($date2 در [[w:fa:Old Style and New Style dates|تقویم جولیان]])',
eo='$date1 ($date2 laŭ la [[w:eo:Julia kalendaro|Julia Kalendaro]])',
es='$date1 ($date2 según el [[w:es:Calendario juliano|calendario juliano]])',
et='$date1 ($date2 [[w:et:Juliuse kalender|Juliuse kalendris]])',
fi='$date1 ($date2 [[w:fi:juliaaninen kalenteri|juliaanisen kalenterin]] mukaan)',
fr='$date1 ($date2 dans le [[w:fr:calendrier julien|calendrier julien]])',
gl='$date1 ($date2 segundo o [[w:gl:Calendario xuliano|calendario xuliano]])',
hu='$date1 ($date2 a [[w:hu:Julianus-naptár|Julián naptárban]])',
hr='$date1 ($date2 prema [[w:hr:Julijanski kalendar|julijanskom kalendaru]])',
he='$date1 ($date2 ב[[w:he:לוח היוליאני|הלוח היוליאני]])',
hsb='$date1 ($date2 po julianiskej protyce)',
it='$date1 ($date2 secondo il [[w:it:Calendario giuliano|calendario giuliano]])',
ja='$date1([[w:ja:ユリウス暦|ユリウス暦]] $date2)',
mk='$date1 ($date2 по [[w:mk:Јулијански календар|Јулијанскиот календар]])',
ml='$date1 ([[w:ml:ജൂലിയൻ കാലഗണനാരീതി|ജൂലിയൻ വർഷത്തിൽ]] $date2)',
ml='$date1 ([[w:Old Style and New Style dates|ജൂലിയൻ കാലഗണനാരീതിയിൽ]] $date2)',
nds='$date1 ($date2 na’n [[w:nds:Juliaansch Klenner|Juliaanschen Klenner]])',
nb='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nn='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nl='$date1 ($date2 volgens de [[w:nl:Juliaanse kalender|Juliaanse kalender]])',
no='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalender]])',
pl='$date1 ($date2 wedle [[w:pl:Daty nowego i starego porządku|kalendarza juliańskiego]])',
pt='$date1 ($date2 no [[w:pt:Mudança para o calendário gregoriano|calendário juliano]])',
qu='$date1 ($date2 [[w:qu:Hulyanu kalindaryu|Hulyanu kalindaryupi]])',
ro='$date1 ($date2 în [[w:ro:calendarul iulian|calendarul iulian]])',
ru='$date1 ($date2 [[w:ru:Юлианский календарь|по ст. ст.]])',
sk='$date1 ($date2 podľa [[w:sk:Juliánsky kalendár|juliánskeho kalendára]])',
sl='$date1 ($date2 po [[w:sl:Julijanski koledar|julijanskem koledarju]])',
sv='$date1 ($date2 [[w:sv:Gamla stilen|gamla stilen]])',
uk='$date1 ($date2 [[w:uk:Юліанський календар|Юліанський календар]])',
ur='$date1 ($date2 حسب [[w:ur:نئے اور پانے انداز کی تاریخ|جولین تقویم]])',
vi='$date1 ($date2 trong [[w:vi:Lịch Julius|lịch Juliêng]])',
['zh-hans']='$date1([[w:Old Style and New Style dates|格里历]]$date2)',
['zh-hant']='$date1([[w:Old Style and New Style dates|格里曆]]$date2)',
default='<span style="color:red">$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])</span>',
},
['ad'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:ад Нараджэньня Хрыста|пасьля Н. Х.]]',
bn ='$date1 খ্রিস্টাব্দ',
ca ='$date1 dC',
cs ='$date1 n. l.',
de ='$date1 n. Chr.',
el ='$date1 μ.Χ.',
en ='$date1 [[w:en:Anno Domini|AD]]',
['en-gb'] ='$date1 [[w:en:Anno Domini|AD]]',
['en-ca'] ='$date1 [[w:en:Anno Domini|AD]]',
eo ='$date1 pK',
es ='$date1 [[w:es:Anno Dómini|d. C.]]',
fa ='$date1 م.',
fi ='$date1 jaa.',
fr ='$date1 ap. J.-C.',
gl ='$date1 d.C.',
he ='$date1 לספירה',
hu ='i. sz. $date1',
hr ='$date1 p. Kr.',
hsb='$date1 po Chr.',
it ='$date1 d.C.',
ja ='西暦$date1',
mk ='$date1 н.е.',
ml ='ക്രി.വ. $date1',
nb ='$date1 e.Kr.',
nl ='$date1 n.Chr.',
nn ='$date1 e.Kr.',
no ='$date1 e.Kr.',
pl ='$date1 [[w:pl:naszej ery|n.e.]]',
ro ='$date1 d.Hr.',
ru ='$date1 н. э.',
sk ='$date1 po Kr.',
sl ='$date1 n. št.',
sv ='$date1 [[w:sv:Efter Kristus|e.Kr.]]',
uk ='$date1 н. е.',
ur ='$date1 ء',
vi ='$date1 [[w:vi:Công Nguyên|CN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元]]$date1',
default='<span style="color:red">$date1 [[w:en:Anno Domini|AD]]</span>',
},
['ah'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:Ісламскі каляндар|хіджры]]',
bn ='$date1 [[w:bn:ইসলামি বর্ষপঞ্জি|হিজরী]]',
cs ='$date1 [[w:cs:Anno Hegirae|AH]]',
en ='$date1 [[w:en:Hijri year|AH]]',
['en-gb'] ='$date1 [[w:en:Hijri year|AH]]',
['en-ca'] ='$date1 [[w:en:Hijri year|AH]]',
eo ='$date1 post la [[w:eo:Heĝiro|Heĝiro]]',
fa ='$date1 ه.ق.',
fr ='$date1 [[w:fr:Calendrier musulman|A.H.]]',
he ="$date1 [[w:he:הלוח המוסלמי|להג'רה]]",
hr ='$date1 god. hidžre',
ja ='[[w:ja:ヒジュラ暦|ヒジュラ暦]]$date1',
ml ='$date1 [[w:ml:ഇസ്ലാമിക കലണ്ടർ|ഹിജ്റ]]',
nb ='$date1 [[w:no:Den muslimske kalenderen|A.H.]]',
nn ='$date1 [[w:nn:Den muslimske kalenderen|A.H.]]',
pl ='$date1 [[w:pl:Kalendarz muzułmański|A.H.]]',
ru ='$date1 [[w:ru:Исламский календарь|хиджры]]',
sk ='$date1 [[w:sk:A. H.|A. H.]]',
sv ='$date1 [[w:sv:Muslimska kalendern|A.H.]]',
uk ='$date1 [[w:uk:Мусульманський календар|хіджри]]',
ur ='$date1 ھ',
vi ='$date1 [[w:vi:Lịch Hồi giáo|AH]]',
['zh-hans'] ='[[w:en:Hijri year|Hijri历]]$date1',
['zh-hant'] ='[[w:en:Hijri year|Hijri曆]]$date1',
default='<span style="color:red">$date1 [[w:en:Hijri year|AH]]</span>',
},
['bc'] = {
nDates = 1,
ar ='$date1 ق.م.',
['be-tarask']='$date1 [[w:be-tarask:да Нараджэньня Хрыста|да Н. Х.]]',
bn ='খ্রিস্টপূর্ব $date1',
ca ='$date1 [[w:ca:Abans de Crist|aC]]',
cs ='$date1 [[w:cs:Př. n. l.|př. n. l.]]',
de ='$date1 [[w:de:v. Chr.|v. Chr.]]',
el ='$date1 π.Χ.',
en ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-gb'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-ca'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
eo ='$date1 aK',
es ='$date1 [[w:es:Antes de Cristo|a. C.]]',
fa ='$date1 پ.م.',
fi ='$date1 eaa.',
fr ='$date1 av. J.-C.',
gl ='$date1 [[w:gl:Antes de Cristo|a.C.]]',
he ='$date1 לפני הספירה',
hr ='$date1 [[w:hr:Pr. Kr.|pr. Kr.]]',
hsb='$date1 do Chr.',
hu ='i. e. $date1',
it ='$date1 [[w:it:Avanti Cristo|a.C.]]',
ja ='[[w:ja:紀元前|紀元前]]$date1',
mk ='$date1 п.н.е.',
ml ='ക്രി.മു. $date1',
nds='$date1 vör uns Tied',
nb ='$date1 f.Kr.',
nl ='$date1 v.Chr.',
nn ='$date1 f.Kr.',
no ='$date1 f.Kr.',
pl ='$date1 [[w:pl:przed naszą erą|p.n.e.]]',
ro ='$date1 î.Hr.',
ru ='$date1 до н. э.',
sk ='$date1 [[w:sk:Pred Kristom|pred Kr.]]',
sl ='$date1 pr. n. št.',
sv ='$date1 [[w:sv:Före Kristus|f.Kr.]]',
uk ='$date1 до н. е.',
ur ='$date1 ق م',
vi ='$date1 [[w:vi:Trước Công Nguyên|TCN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元前]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元前]]$date1',
default='<span style="color:red">$date1 [[w:en:Ante Christum Natum|BC]]</span>',
},
['bp'] = {
nDates = 1,
ar ='$date1 [[w:ar:قبل الحاضر|قبل الحاضر]]',
be ='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
['be-tarask']='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
bn ='বর্তমানের আগে $date1',
ca ='$date1 [[w:ca:BP (unitat de temps)|BP]]',
cs ='$date1 [[w:cs:B. P.|B. P.]]',
de ='$date1 [[w:de:Before Present|BP]]',
el ='$date1 [[w:en:Before Present|BP]]',
en ='$date1 [[w:en:Before Present|BP]]',
['en-gb'] ='$date1 [[w:en:Before Present|BP]]',
['en-ca'] ='$date1 [[w:en:Before Present|BP]]',
eo='$date1 [[w:eo:Before Present|BP]]',
es ='$date1 [[w:es:Before Present|BP]]',
et ='$date1 [[w:et:Before Present|BP]]',
fa ='$date1 پیش از اکنون',
fi ='$date1 [[w:fi:BP (ajanlasku)|BP]]',
fr ='$date1 [[w:fr:Avant le présent|avant le présent]]',
hr ='$date1 [[w:hr:BP|prije današnjice]]',
it ='$date1 [[w:it:Before Present|BP]]',
ja ='$date1 [[w:ja:BP (年代測定)|BP]]',
lv ='$date1 [[w:lv:Pirms mūsdienām|Pirms mūsdienām]]',
mk ='$date1 [[w:mk:Пред сегашноста|п.с.]]',
ml ='[[w:en:Before Present|ഇന്നത്തെ കാലത്തിനു മുമ്പ്]] $date1',
nb ='$date1 [[w:no:Before Present|BP]]',
nn ='$date1 [[w:nn:Before Present|BP]]',
nl ='$date1 [[w:nl:Before Present|BP]]',
no ='$date1 [[w:no:Before Present|BP]]',
pl ='$date1 [[w:pl:Before Present|BP]]',
pt ='$date1 [[w:pt:Antes do Presente|Antes do Presente]]',
ru ='$date1 [[w:ru:До настоящего времени|до н. в.]]',
sk ='$date1 [[w:sk:BP (čas)|BP]]',
sl ='$date1 [[w:sl:pred sedanjostjo|p. s.]]',
sr ='$date1 [[w:sr:Прије садашњости|Прије садашњости]]',
sv ='$date1 [[w:sv:B.P.|B.P.]]',
uk ='$date1 [[w:uk:Років тому|Років тому]]',
ur ='$date1 قبل حاضر',
vi ='$date1 [[w:en:Before Present|trước ngày nay]]',
['zh-hans'] ='[[w:zh:距今|距今]]$date1',
['zh-hant'] ='[[w:zh:距今|距今]]$date1',
default='<span style="color:red">$date1 [[w:en:Before Present|BP]]</span>',
},
}
-- List of possible string inputs to "adj", "junction", "era", and "precision" fields
-- Each term is matched to its synonym and the role: a - adjective, e - era
-- p - precision, c - calendar and combos, r - certainty and j - conjunction
p.Synonyms = {
-- adjectives and prepositions
['quarter1']={'1quarter','a'},
['1stquarter']={'1quarter','a'},
['1st quarter']={'1quarter','a'},
['first quarter']={'1quarter','a'},
['1quarter']={'1quarter','a'},
['quarter2']={'2quarter','a'},
['2ndquarter']={'2quarter','a'},
['2nd quarter']={'2quarter','a'},
['second quarter']={'2quarter','a'},
['2quarter']={'2quarter','a'},
['quarter3']={'3quarter','a'},
['3rdquarter']={'3quarter','a'},
['3rd quarter']={'3quarter','a'},
['third quarter']={'3quarter','a'},
['3quarter']={'3quarter','a'},
['quarter4']={'4quarter','a'},
['4thquarter']={'4quarter','a'},
['4th quarter']={'4quarter','a'},
['fourth quarter']={'4quarter','a'},
['4quarter']={'4quarter','a'},
['spring']={'spring','a'},
['summer']={'summer','a'},
['fall']={'autumn','a'},
['autumn']={'autumn','a'},
['winter']={'winter','a'},
['beginning']={'early','a'},
['early']={'early','a'},
['1half']={'firsthalf','a'},
['1sthalf']={'firsthalf','a'},
['1st half']={'firsthalf','a'},
['firsthalf']={'firsthalf','a'},
['first half']={'firsthalf','a'},
['end']={'late','a'},
['late']={'late','a'},
['middle']={'mid','a'},
['mid']={'mid','a'},
['2half']={'secondhalf','a'},
['2ndhalf']={'secondhalf','a'},
['2nd half']={'secondhalf','a'},
['secondhalf']={'secondhalf','a'},
['second half']={'secondhalf','a'},
['<']={'before','a'},
['b']={'before','a'},
['before']={'before','a'},
['>']={'after','a'},
['a']={'after','a'},
['after']={'after','a'},
['~']={'circa','a'},
['c']={'circa','a'},
['ca']={'circa','a'},
['circa']={'circa','a'},
['by']={'by','a'},
['from']={'from','a'},
['until']={'until','a'},
['uncertain']={'uncertain','r'},
['possibly']={'possibly','r'},
['probably']={'probably','r'},
['presumably']={'presumably','r'},
-- eras
['bc']={'bc','e'},
['bce']={'bc','e'},
['before present']={'bp','e'},
['bp']={'bp','e'},
['ce']={'ad','e'},
['ad']={'ad','e'},
['ah']={'ah','e'},
-- conjunctions
['&']={'and','j'},
['and']={'and','j'},
['or']={'or','j'},
['-']={'from-until','j'},
['–']={'from-until','j'},
['from-until']={'from-until','j'},
['between']={'between','j'},
['circa2']={'circa2','a'},
-- precision
['day']={'day','p'},
['month']={'month','p'},
['year']={'year','p'},
['s']={'decade','p'},
['decade']={'decade','p'},
['century']={'century','p'},
['millennium']={'millennium','p'},
['million']={'million','p'},
['billion']={'billion','p'},
-- calendar
['julian']={'julian','c'},
['islamic']={'islamic','c'},
['hijri']={'islamic','c'},
-- "Turn of the ..." options are a combos which will be handled in similar way as calendars
['turn of the century']={'turn of the century','c'},
['century turn']={'turn of the century','c'},
['turn of the decade']={'turn of the decade','c'},
['decade turn']={'turn of the decade','c'},
['turn of the year']={'turn of the year','c'},
['year turn']={'turn of the year','c'},
-- other dates which will be handled in similar way as calendars
['?']={'unknown','c'},
['unknown']={'unknown','c'},
['unknown year']={'year unknown','c'},
['year unknown']={'year unknown','c'},
}
return p
4v7usasdo7wjfkksm29h8yxtd6em2e0
803608
803607
2019-02-25T21:49:42Z
en>Pppery
0
Re-lazifying
803608
Scribunto
text/plain
--[[
__ __ _ _ ___ _ ___ __ _ _ _
| \/ | ___ __| |_ _| | ___ _|_ _/ |( _ ) _ __ / /__ ___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_)| || |/ _ \| '_ \ / / __/ _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_ | || | (_) | | | |/ / (_| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)___|_|\___/|_| |_/_/ \___\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This code was copied from Commons please request changes there.
Maintainers:
*Jarekt
]]
local function Ordinal(...)
return require('Module:Ordinal-cd')._Ordinal(...)
end
local function Roman(...)
return require('Module:Roman')._Numeral(...)
end
-- ==========================================================================
-- === Local Helper functions ==============================================
-- ==========================================================================
-- form of French "of" based on "precision", which is wikibase compatible date precision format:
-- 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- 1 2 3 4 5 mill cent deca year month day
local frOf = {'du','du','du','du','du', 'du', 'du', 'de', 'de', 'de', 'du'}
local function decYear(state,lang,case)
--Declension of word "year" in several languages
if state.precision == 9 then
yearCase = {
fi={xxx='vuosien', gen='vuoden', ill='vuoteen', ptv='vuotta', ela='vuodesta', ine='vuonna'},
sl={insP='letoma', ins='letom', gen='leta', loc='letu'},
hsb={insP='lětami', ins='lětom', gen='lěta', loc='lěće'}
}
return yearCase[lang][case] .. ' '
else
return ''
end
end
local function decCentury(lang,case)
--Declension of word "century" in several languages
centuryCase = {
pl={nom='wiek', gen='wieku', ins='wiekiem', loc='wieku', default = 'wiek'},
hsb={nom='lětstotk', gen='lětstotka', ins='lětstotkom', loc='lětstotku', default = 'lětstotk'}
}
return centuryCase[lang][case] or centuryCase[lang]['default']
end
local function decMillennium(lang,case)
--Declension of word "century" in several languages
millenniumCase = {
pl={nom='tysiąclecie', gen='tysiąclecia', ins='tysiącleciem', loc='tysiącleciu', default = 'tysiąclecie'},
hsb={nom='lěttysac', gen='lěttysaca', ins='lěttysacom', loc='lěttysacu', default = 'lěttysac'}
}
return millenniumCase[lang][case] or millenniumCase[lang]['default']
end
-- ==========================================================================
-- === Output data structure ===============================================
-- ==========================================================================
local p = {}
--[[
Multiple formats are possible:
1) string with key words "$date1" and "$date2" which will be replaced later by the date strings
2) function which can be used to create output string
* Function INPUTS:
- date1 and date2 string with date strings. "$date1" and "$date2" can also be used which will be replaced latter
- state is a table with full description of the phrase we are trying to construct. It includes state.precision1 (and 2)
which is wikibase compatible date precision format: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
3) table with following fields:
* table[1] is either string or a function
* table.case is a 2 element table with Grammatical cases used by given language to alter
month name or other adjective related to date # 1 and 2
]]
p.Translations = {
-- ==========================================
-- === Two date phrases =====================
-- ==========================================
['from-until'] = {
nDates = 2,
ar='من $date1 إلى $date2',
['be-tarask']={'з $date1 да $date2', case={'gen','gen'}},
bn='$date1 থেকে $date2 পর্যন্ত',
ca='$date1 fins $date2',
cs='$date1 až $date2',
da='fra $date1 til $date2',
de='$date1 bis $date2',
el='από $date1 έως $date2',
en='from $date1 until $date2',
['en-gb']='from $date1 until $date2',
['en-ca']='from $date1 until $date2',
eo='de $date1 al $date2',
es='de $date1 a $date2',
et='$date1 kuni $date2',
fa='از $date1 تا $date2',
fi='$date1–$date2', -- välisenä aikana
fr=function(date1, date2, state) -- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision > 10 then -- precision > month
return 'du $date1 au $date2'
else -- "DE septebmbre" / "D'octobre"
return require("Module:Linguistic").of(date1, 'fr') .. " à $date2"
end
end,
gl='de $date1 a $date2', --Template:From until used 'desde $date1 ata $date2'
he='מ- $date1 עד $date2',
hr='od $date1 do $date2',
hsb={'wot $date1 do $date2', case={'gen','gen'}},
hu='$date1 – $date2',
it='dal $date1 al $date2',
ja='$date1から$date2まで',
mk='од $date1 до $date2',
ml='$date1 മുതൽ $date2 വരെ',
nb='fra $date1 til $date2',
nds='$date1 bet $date2',
nn='fra $date1 til $date2',
nl='$date1 tot $date2',
no='fra $date1 til $date2',
pl={'od $date1 do $date2', case={'gen','gen'}},
ro='din $date1 până în $date2',
pt='de $date1 a $date2',
qu="$date1 p'unchawmanta $date2 p\'unchawkama",
ru={'с $date1 по $date2', case={'gen','gen'}},
sk={'od $date1 do $date2', case={'gen','gen'}},
sl={'od $date1 do $date2', case={'gen','gen'}},
sv='från $date1 till $date2',
uk={'з $date1 до $date2', case={'gen','gen'}},
ur='از $date1 تا $date2',
vi='từ $date1 đến $date2',
['zh-hans']='从$date1到$date2',
['zh-hant']='從$date1到$date2',
default='<span style="color:red">$date1 - $date2</span>',
},
['between']={
nDates = 2,
ar='بين $date1 و $date2',
['be-tarask']={'між $date1 і $date2', case={'ins','ins'}},
bn='$date1 এবং $date2-এর মাঝামাঝি',
ca='entre $date1 i $date2',
cs={'mezi $date1 a $date2', case={'ins','ins'}},
da='mellem $date1 og $date2',
de='zwischen $date1 und $date2',
el='μεταξύ $date1 και $date2',
en='between $date1 and $date2',
['en-gb']='between $date1 and $date2',
['en-ca']='between $date1 and $date2',
eo='inter $date1 kaj $date2',
es='entre $date1 y $date2',
et='$date1 ja $date2 vahel',
fa='بین $date1 و $date2',
fi={function(date1, date2, state) return decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='entre $date1 et $date2',
gl='entre $date1 e $date2',
he='בין $date1 ו- $date2',
hr='između $date1 i $date2',
hsb={'mjez $date1 a $date2', case={'ins','ins'}},
hu='$date1 és $date2 között',
hi='$date1 और $date2 के बीच',
it='tra il $date1 e il $date2',
ja='$date1と$date2の間',
mk='помеѓу $date1 и $date2',
ml='$date1, $date2 എന്നിവയ്ക്കിടയ്ക്ക്',
nb='mellom $date1 og $date2',
nds='twischen $date1 un $date2',
nn='mellom $date1 og $date2',
nl='tussen $date1 en $date2',
no='mellom $date1 og $date2',
pl={'między $date1 a $date2', case={'ins','ins'}},
ro='între $date1 și $date2',
pt='entre $date1 e $date2',
qu="$date1 p'unchawpa $date2 p'unchawpapas chawpin",
ru={'между $date1 и $date2', case={'ins','ins'}},
sk={'medzi $date1 a $date2', case={'ins','ins'}},
sl={function(date1, date2, state) return 'med ' .. decYear(state,'sl','insP') .. '$date1 in $date2' end, case={'ins','ins'}},
sv='mellan $date1 och $date2',
uk={'між $date1 та $date2', case={'ins','ins'}},
ur='بین $date1 و $date2',
vec='tra el $date1 e el $date2',
vi='giữa $date1 và $date2',
['zh-hans']='在$date1到$date2之间',
['zh-hant']='在$date1到$date2之間',
default='<span style="color:red">$date1 - $date2</span>',
},
['or'] = {
nDates = 2,
ar='$date1 أو $date2',
['be-tarask']='$date1 ці $date2',
bn='$date1 অথবা $date2',
ca='$date1 o $date2',
cs='$date1 nebo $date2',
da='$date1 eller $date2',
de='$date1 oder $date2',
el='$date1 ή $date2',
en='$date1 or $date2',
['en-gb']='$date1 or $date2',
['en-ca']='$date1 or $date2',
eo='$date1 aŭ $date2',
es='$date1 o $date2',
et='$date1 või $date2',
fa='$date1 یا $date2',
fi='$date1 tai $date2',
fr='$date1 ou $date2',
gl='$date1 ou $date2',
he='$date1 או $date2',
hr='$date1 ili $date2',
hsb='$date1 abo $date2',
hu='$date1 vagy $date2',
it='$date1 o $date2',
ja='$date1または$date2',
mk='$date1 или $date2',
ml='$date1 അഥവാ $date2',
nb='$date1 eller $date2',
nn='$date1 eller $date2',
nds='$date1 oder $date2',
nl='$date1 of $date2',
no='$date1 eller $date2',
pl='$date1 lub $date2',
pt='$date1 ou $date2',
qu='$date1 icha $date2',
ro='$date1 sau $date2',
ru='$date1 или $date2',
sk='$date1 alebo $date2',
sl='$date1 ali $date2',
sv='$date1 eller $date2',
uk='$date1 або $date2',
ur='$date1 یا $date2',
th='$date1 หรือ $date2',
vi='$date1 hoặc $date2',
['zh-hans']='$date1或$date2',
['zh-hant']='$date1或$date2',
default=function(date1, date2, state) return require("Module:linguistic").conj({date1, date2}, state.lang, 'or') end,
},
['and'] = {
nDates = 2,
ar='$date1 و $date2',
['be-tarask']='$date1 і $date2',
bn='$date1 এবং $date2',
ca='$date1 i $date2',
cs='$date1 a $date2',
da='$date1 og $date2',
de='$date1 und $date2',
el='$date1 και $date2',
en='$date1 and $date2',
['en-gb']='$date1 and $date2',
['en-ca']='$date1 and $date2',
eo='$date1 kaj $date2',
es='$date1 y $date2',
et='$date1 ja $date2',
fa='$date1 و $date2',
fi='$date1 ja $date2',
fr='$date1 et $date2',
gl='$date1 e $date2',
he='$date1 ו- $date2',
hu='$date1 és $date2',
hsb='$date1 a $date2',
it='$date1 e $date2',
ja='$date1および$date2',
mk='$date1 и $date2',
ml='$date1 ഒപ്പം $date2',
nb='$date1 og $date2',
nn='$date1 og $date2',
nds='$date1 un $date2',
nl='$date1 en $date2',
no='$date1 og $date2',
pl='$date1 i $date2',
pt='$date1 e $date2',
qu='$date1wan $date2pas',
ro='$date1 și $date2',
ru='$date1 и $date2',
sk='$date1 a $date2',
sl='$date1 in $date2',
sv='$date1 och $date2',
uk='$date1 та $date2',
ur='$date1 و $date2',
vi='$date1 và $date2',
['zh-hans']='$date1和$date2',
['zh-hant']='$date1和$date2',
default=function(date1, date2, state) return require("Module:linguistic").conj({date1, date2}, state.lang, 'and') end,
},
-- ==========================================
-- === Prepositions =========================
-- ==========================================
['by'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'да $date1', case={'gen',''}},
bn='$date1 কর্তৃক',
ca='vers $date1',
cs={'kolem $date1', case={'gen',''}},
da='mod $date1',
de='spätestens $date1',
el='έως $date1',
en='by $date1',
['en-gb']='by $date1',
['en-ca']='by $date1',
eo='ne post $date1',
es='para $date1',
et='hiljemalt $date1',
fa='توسط $date1',
fi={function(date1, state) return decYear(state,'fi','ill') .. '$date1 mennessä' end, case={'ill',''}},
fr='$date1 (au plus tard)',
gl='cara a $date1',
he='עד $date1',
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='entro il $date1',
ja='$date1まで',
mk='кон $date1',
ml='$date1-ൽ',
nb='innen $date1',
nn='innan $date1',
nds='$date1 oder vördem al',
nl='tegen $date1',
no='innen $date1',
pl={'przed $date1', case={'ins',''}},
pt='por $date1',
qu="$date1 p'unchawkama",
ro='de $date1',
ru={'к $date1', case={'dat',''}},
sk={'okolo $date1', case={'gen',''}},
sl={function(date1, state) return 'najpozneje ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='senast $date1',
uk={'до $date1', case={'dat',''}},
ur='توسط $date1',
vi='vào $date1', -- need to insert "đã" somewhere later in the sentence -->
['zh-hans']='$date1前后',
['zh-hant']='$date1前后',
default='<span style="color:red">by $date1</span>',
},
['before'] = {
nDates = 1,
ar='قبل $date1',
['be-tarask']={'перад $date1', case={'ins',''}},
bn='$date1-এর আগে',
bg='Преди $date1',
ca='abans $date1',
cs={'před $date1', case={'ins',''}},
da='før $date1',
de='vor $date1',
el='πριν από $date1',
en='before $date1',
['en-gb']='before $date1',
['en-ca']='before $date1',
eo='antaŭ $date1',
es='antes de $date1',
et='enne $date1',
fa='پیش از $date1',
fi={function(date1, state) return 'ennen ' .. decYear(state,'fi','ptv') .. '$date1' end, case={'ptv',''}},
fr='avant $date1',
gl='antes do $date1',
he='לפני $date1',
hr='prije $date1',
hsb={'do $date1', case={'gen',''}},
hu='$date1 előtt',
it='prima del $date1',
ja='$date1より前',
mk='пред $date1',
ml='$date1-നു മുമ്പ്',
nb='før $date1',
nn='før $date1',
nds='vör $date1',
nl='voor $date1',
no='før $date1',
pl={'przed $date1', case={'ins',''}},
pt='antes de $date1',
qu="$date1 p'unchaw ñawpan",
ro='înainte de $date1',
ru={'ранее $date1', case={'gen',''}},
sk={'pred $date1', case={'ins',''}},
sl={function(date1, state) return 'pred ' .. decYear(state,'sl','ins') .. ' $date1' end, case={'ins',''}},
sv='före $date1',
uk={'до $date1', case={'gen',''}},
ur='قبل $date1',
vi='trước $date1',
['zh-hans']='在$date1之前',
['zh-hant']='在$date1之前',
default='<span style="color:red">before $date1</span>',
},
['from'] = {
nDates = 1,
ar='من $date1',
['be-tarask']={'з $date1', case={'gen',''}},
bn='$date1 থেকে',
cs={'od $date1', case={'gen',''}},
de='Seit $date1',
el='από $date1',
en='from $date1',
['en-gb']='from $date1',
['en-ca']='from $date1',
eo='ekde $date1',
es='desde $date1',
et='alates $date1',
fa='از $date1',
fi={function(date1, state) return decYear(state,'fi','ela') .. '$date1 alkaen' end, case={'ela',''}},
fr= function(date1, state) return 'à partir '.. frOf[state.precision] ..' $date1' end,
gl='desde o $date1',
he='מ- $date1',
hr='od $date1',
hsb={'wot $date1', case={'gen',''}},
it='dal $date1',
ja='$date1から',
nds='vonaf $date1',
mk='од $date1',
nb='fra $date1',
nn='frå $date1',
ml='$date1 മുതൽ',
nl='vanaf $date1',
no='fra $date1',
pl={'od $date1', case={'gen',''}},
qu="$date1 p'unchawmanta",
ro='din $date1',
ru={'С $date1', case={'gen',''}},
sk={'od $date1', case={'gen',''}},
sl={function(date1, state) return 'od ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='från $date1',
uk={'з $date1', case={'gen',''}},
ur='از $date1',
vi='từ $date1',
['zh-hans']='自$date1',
['zh-hant']='自$date1',
default='<span style="color:red">from $date1</span>',
},
['until'] = {
nDates = 1,
['be-tarask']={'усутыч да $date1', case={'gen',''}},
bn='$date1 পর্যন্ত',
cs={'do $date1', case={'gen', ''}},
de='bis $date1',
en='until $date1',
['en-gb']='until $date1',
['en-ca']='until $date1',
eo='ĝis $date1',
es='hasta $date1',
fr=function(date1, state)
if state.precision > 10 then -- precision > month
return "jusqu'au $date1"
else
return "jusqu'à $date1"
end
end,
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='al $date1',
nb='til $date1',
nl='tot $date1',
nn='til $date1',
ml='$date1 വരെ',
pl={'do $date1', case={'gen',''}},
ru={'вплоть до $date1', case={'gen',''}},
sk={'do $date1', case={'gen', ''}},
uk={'аж до $date1', case={'gen',''}},
ur='تک $date1',
['zh-hans']='直到$date1',
['zh-hant']='直到$date1',
default='<span style="color:red">until $date1</span>',
},
['after'] = {
nDates = 1,
ar='بعد $date1',
['be-tarask']={'пасьля $date1', case={'gen',''}},
bn='$date1-এর পরে',
ca='després de $date1',
cs={'po $date1', case={'loc',''}},
de='nach $date1',
da='efter $date1',
el='μετά από $date1',
en='after $date1',
['en-gb']='after $date1',
['en-ca']='after $date1',
eo='post $date1',
es='después de $date1',
et='pärast $date1',
fa='پس از $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 jälkeen' end, case={'gen',''}},
fr='après $date1',
gl='despois do $date1',
he='לאחר $date1',
hr='nakon $date1',
hu='$date1 után',
hsb={'po $date1', case={'loc',''}},
it='dopo il $date1',
ja='$date1より後',
mk='по $date1',
ml='$date1-നു ശേഷം',
nb='etter $date1',
nn='etter $date1',
nds='na $date1',
nl='na $date1',
no='etter $date1',
pl={'po $date1', case={'loc',''}},
pt='após $date1',
qu="$date1 p'unchaw qhipan",
ro='după $date1',
ru={'после $date1', case={'gen',''}},
sk={'po $date1', case={'loc',''}},
sl={function(date1, state) return 'po ' .. decYear(state,'sl','loc') .. ' $date1' end, case={'loc',''}},
sv='efter $date1',
uk={'після $date1', case={'gen',''}},
ur='بعد $date1',
vi='sau $date1',
['zh-hans']='在$date1之后',
['zh-hant']='在$date1之後',
default='<span style="color:red">after $date1</span>',
},
-- ==========================================
-- === Adjectives ===========================
-- ==========================================
['early'] = {
nDates = 1,
ar='بداية $date1',
['be-tarask']={'пачатак $date1', case={'gen',''}},
bn='$date1-এর শুরুতে',
ca='al principi de $date1',
cs={'začátek $date1', case={'gen',''}},
da='tidlig $date1',
de='Anfang $date1',
el='αρχές του $date1',
en='early $date1',
['en-gb']='early $date1',
['en-ca']='early $date1',
eo='komence de $date1',
es='a principios de $date1',
et='$date1 algus',
fa='اوایل $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alku' end, case={'gen',''}},
fr='début $date1',
gl='a principios de $date1',
he='תחילת $date1',
hr='početak $date1',
hsb={'spočatk $date1', case={'gen',''}},
hu='$date1 elején',
it='inizio del $date1',
ja='$date1初頭',
mk='почеток на $date1',
ml='$date1 ആദ്യപാദം',
nb='tidlig $date1',
nn='tidleg $date1',
nds='Anfang $date1',
nl='begin $date1',
no='tidlig $date1',
pl={'początek $date1', case={'gen',''}},
pt='a princípios de $date1',
qu="$date1 ñawpaq p'unchawkunapi",
ro='începând cu $date1',
ru={'начало $date1', case={'gen',''}},
sk={'začiatok $date1', case={'gen',''}},
sl=function(date1, state)-- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision == 9 then -- precision > month
return 'zgodnje leto $date1'
else -- "DE septebmbre" / "D'octobre"
return 'zgodnji $date1'
end
end,
sv='början av $date1',
uk={'початок $date1', case={'gen',''}},
ur='ابتدا $date1',
vi='đầu năm $date1',
['zh-hans']='$date1早期',
['zh-hant']='$date1早期',
default='<span style="color:red">early $date1</span>',
},
['mid'] = {
nDates = 1,
ar='منتصف $date1',
['be-tarask']={'сярэдзіна $date1', case={'gen',''}},
bn='$date1-এর মাঝামাঝি',
ca='a mitjan $date1',
cs={'polovina $date1', case={'gen',''}},
da='midten af $date1',
de='Mitte $date1',
el='μέσα του $date1',
en='mid $date1',
['en-gb']='mid $date1',
['en-ca']='mid $date1',
eo='meze de $date1',
es='a mediados de $date1',
et='$date1 keskel',
fa='اواسط $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 puoliväli' end, case={'gen',''}},
fr='mi $date1',
gl='a mediados de $date1',
he='אמצע $date1',
hr='sredina $date1',
hsb={'srjedź $date1', case={'gen',''}},
hu='$date1 közepén',
it='metà del $date1',
ja='$date1半ば',
mk='средина на $date1',
ml='$date1 മദ്ധ്യം',
nb='midten av $date1',
nn='midten av $date1',
nds='Midd $date1',
nl='midden $date1',
no='midten av $date1',
pl={'środek $date1', case={'gen',''}},
pt='meados de $date1',
qu="$date1 chawpin p'unchawkunapi",
ro='mijlocul lui $date1',
ru={'середина $date1', case={'gen',''}},
sk={'polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'sredina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='mitten av $date1',
uk={'середина $date1', case={'gen',''}},
ur='وسط $date1',
vi='giữa năm $date1',
['zh-hans']='$date1中期',
['zh-hant']='$date1中期',
default='<span style="color:red">mid $date1</span>',
},
['late'] = {
nDates = 1,
ar='نهاية $date1',
bn='$date1-এর শেষে',
['be-tarask']={'канец $date1', case={'gen',''}},
ca='al final de $date1',
cs='konec $date1',
da='sen $date1',
de='Ende $date1',
el='τέλη του $date1',
en='late $date1',
['en-gb']='late $date1',
['en-ca']='late $date1',
eo='fine de $date1',
es='a fines de $date1',
et='$date1 lõpp',
fa='اواخر $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppu' end, case={'gen',''}},
fr='fin $date1',
gl='a finais de $date1',
he='סוף $date1',
hr='kraj $date1',
hsb={'kónc $date1', case={'gen',''}},
hu='$date1 végén',
it='fine del $date1',
ja='$date1末',
mk='крајот на $date1',
ml='$date1 അന്ത്യപാദം',
nb='sent $date1',
no='seint $date1',
nds='Enn $date1',
nl='eind $date1',
no='sent $date1',
pl={'koniec $date1', case={'gen',''}},
pt='ao final de $date1',
qu="$date1 qhipaq p'unchawkunapi",
ro='sfârșitul lui $date1',
ru={'конец $date1', case={'gen',''}},
sk={'koniec $date1', case={'gen',''}},
sl={function(date1, state) return 'pozno ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='slutet av $date1',
uk={'кінець $date1', case={'gen',''}},
ur='اخیر $date1',
vi='cuối năm $date1',
['zh-hans']='$date1晚期',
['zh-hant']='$date1晚期',
default='<span style="color:red">late $date1</span>',
},
['circa'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'каля $date1', case={'gen',''}},
bn='প্রায় $date1',
ca='cap a $date1',
cs='cca $date1',
da='ca. $date1',
de='etwa $date1',
el='περ. $date1',
en='circa $date1',
['en-gb']='circa $date1',
['en-ca']='circa $date1',
eo='ĉirkaŭ $date1',
es='hacia $date1', -- suggestion use "hacia"/"hacia el" or leave the latin circa or ca.
et='ca. $date1',
fa='حوالی $date1', -- template:Circa used 'حدود $date1'
fi={function(date1, state) return 'suunnilleen ' .. decYear(state,'fi','ine') .. '$date1' end, case={'ine',''}}, -- template:Circa used 'noin $date1'
fr=function(date1, state)
if state.precision >= 11 then -- precision >= day
return 'vers le $date1'
else
return 'vers $date1'
end
end,
gl='entre $date1', -- template:Circa used 'cara a $date1'
he='בסביבות $date1',
hr='oko $date1',
hsb={'wokoło $date1', case={'gen',''}},
hu='$date1 körül',
it='$date1 circa',
ja='$date1頃',
ko='$date1경',
mk='~ $date1', -- template:Circa used 'o. $date1'
ml= 'ഉദ്ദേശം $date1',
nb='ca. $date1',
nn='ca. $date1',
nds='üm un bi $date1',
nl='ca. $date1',
no='ca. $date1',
pl={'około $date1', case={'gen',''}},
pt='cerca de $date1',
qu='$date1chá',
ro='circa $date1',
ru={'около $date1', case={'gen',''}},
sk='cca $date1',
sl={function(date1, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='c:a $date1', -- template:Circa used 'ca $date1'
uk={'близько $date1', case={'gen', ''}},
ur='قریباً $date1',
vi='vào khoảng $date1',
['zh-hans']='约$date1',
['zh-hant']='約$date1',
default='<span style="color:red">circa $date1</span>',
},
['uncertain'] = {
nDates = 1,
['zh-hans']='$date1(?)',
['zh-hant']='$date1(?)',
default='$date1 (?)',
},
probably = { -- see Q56644435
nDates = 1,
ast = 'probablemente $date1',
bn = 'সম্ভবত $date1',
ca = 'probablement $date1',
cs = 'pravděpodobně $date1',
de = 'wahrscheinlich $date1',
el = 'πιθανόν $date1',
en = 'probably $date1',
['en-gb']='probably $date1',
['en-ca']='probably $date1',
eo = 'probable $date1',
es = 'probable $date1',
fa = 'احتمالاً $date1',
fr = 'probablement $date1',
he = 'כפי הנראה $date1',
hu = 'feltehetőleg $date1',
it = 'probabile $date1',
mk = 'веројатно $date1',
ml = 'ഒരുപക്ഷേ $date1',
nl = 'waarschijnlijk $date1',
pl = 'prawdopodobnie $date1',
pt = 'provavelmente $date1',
ru = 'вероятно $date1',
sv = 'antagligen $date1',
uk = 'ймовірно $date1',
default='<span style="color:red">probably $date1</span>',
},
possibly = { -- see Q30230067
nDates = 1,
ast = 'posiblemente $date1',
bn = 'যথাসম্ভব $date1',
cs = 'možná $date1',
de = 'möglicherweise $date1',
en = 'possibly $date1',
['en-gb']='possibly $date1',
['en-ca']='possibly $date1',
eo = 'eble $date1',
el = 'πιθανόν $date1',
fa = 'احتمالاً $date1',
fr = 'peut-être $date1',
hu = 'talán $date1',
mk = 'можеби $date1',
ml = 'മിക്കവാറും $date1',
nl = 'mogelijk $date1',
pl = 'być może $date1',
pt = 'possivelmente $date1',
ro = 'poate $date1',
ru = 'возможно $date1',
sv = 'möjligen $date1',
uk = 'можливо $date1',
default='<span style="color:red">possibly $date1</span>',
},
presumably = { -- see Q18122778
nDates = 1,
ast = 'presumiblemente $date1',
de = 'vermutlich $date1',
en = 'presumably $date1',
['en-gb']='presumably $date1',
['en-ca']='presumably $date1',
fr = 'présumé $date1',
it = 'presumibilmente $date1',
ml = 'ഊഹപ്രകാരം $date1',
ru = 'предположительно $date1',
pl = 'przypuszczalnie $date1',
uk = 'здогадно $date1',
default='<span style="color:red">presumably $date1</span>',
},
['year unknown']= {
nDates = 0,
ca='Any desconegut',
['be-tarask']='Год невядомы',
bn='অজানা বছর',
cs='neznámý rok',
de='Jahr unbekannt',
el='άγνωστο έτος',
en='year unknown',
['en-gb']='year unknown',
['en-ca']='year unknown',
eo='jaro nesciata',
es='Año desconocido',
fa='سال نامعلوم',
fi='tuntematon vuosi',
fr='Année inconnue',
he='שנה לא ידועה',
hsb='lěto njeznate',
hr='nepoznata godina',
it='anno sconosciuto',
ja='年代不明',
la='Sine anno',
ml='വർഷം ലഭ്യമല്ല',
mk='непозната година',
nb='ukjent år',
nn='ukjent år',
nl='zonder jaar',
no='ukjent år',
pl='rok nieznany',
qu='Mana riqsisqa wata',
ru='Год неизвестен',
sk='neznámy rok',
sl='neznano leto',
sv='Okänt år',
uk='рік невідомий',
ur='سال نامعلوم',
vi='không rõ năm',
['zh-hans']='年份未知',
['zh-hant']='年份未知',
default='<span style="color:red">year unknown</span>',
},
-- ==========================================
-- === Parts of a whole =====================
-- ==========================================
['firsthalf'] = {
nDates = 1,
ar='النصف الأول من $date1',
['be-tarask']={'першая палова $date1', case={'gen',''}},
bn='$date1-এর প্রথমার্ধ',
ca='1a meitat de $date1',
cs={'1. polovina $date1', case={'gen',''}},
da='1. halvdel af $date1',
de='1. Hälfte $date1',
el='πρώτο μισό του $date1',
en='first half of $date1',
['en-gb']='first half of $date1',
['en-ca']='first half of $date1',
eo='unua duono de $date1',
es='primera mitad de $date1',
et='$date1 esimene pool',
fa='نیمهٔ نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alkupuolisko' end, case={'gen',''}},
fr='1<sup>re</sup> moitié de $date1',
gl='primeira metade de $date1',
he='במחצית הראשונה של $date1',
hsb={'prěnja połojca $date1', case={'gen',''}},
hu='$date1 első felében',
hr='prva polovina $date1',
it='prima metà del $date1',
ja='$date1上半期',
mk='прва половина на $date1',
ml='$date1 ആദ്യപകുതി',
nb='første halvdel av $date1',
nn='fyrste halvdelen av $date1',
nds='1. Helft $date1',
nl='eerste helft $date1',
no='første halvdel av $date1',
pl={'pierwsza połowa $date1', case={'gen',''}},
pt='primeira metade de $date1',
qu='$date1 ñawpaq kuskanpi',
ro='prima jumătate a lui $date1',
ru={'первая половина $date1', case={'gen',''}},
sk={'1. polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'prva polovica ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första halvan av $date1',
uk={'перша половина $date1', case={'gen',''}},
ur='نصف اول $date1',
vi='nửa đầu năm $date1',
['zh-hans']='$date1上半期',
['zh-hant']='$date1上半期',
default='<span style="color:red">first half of $date1</span>',
},
['secondhalf'] = {
nDates = 1,
ar='النصف الثاني من $date1',
['be-tarask']={'другая палова $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয়ার্ধ',
ca='2a meitat de $date1',
cs={'2. polovina $date1', case={'gen',''}},
da='2. halvdel af $date1',
de='2. Hälfte $date1',
el='δεύτερο μισό του $date1',
en='second half of $date1',
['en-gb']='second half of $date1',
['en-ca']='second half of $date1',
eo='dua duono de $date1',
es='segunda mitad de $date1',
et='$date1 teine pool',
fa='نیمهٔ دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppupuolisko' end, case={'gen',''}},
fr='2<sup>e</sup> moitié de $date1',
gl='segunda metade de $date1',
he='במחצית השנייה של $date1',
hsb={'druha połojca $date1', case={'gen',''}},
hr='druga polovina $date1',
hu='$date1 második felében',
it='seconda metà del $date1',
ja='$date1下半期',
mk='втора половина на $date1',
ml='$date1 രണ്ടാം പകുതി',
nb='andre halvdel av $date1',
nn='andre halvdelen av $date1',
nds='2. Helft vun $date1',
nl='tweede helft $date1',
no='andre halvdel av $date1',
pl={'druga połowa $date1', case={'gen',''}},
pt='segunda metade de $date1',
qu='$date1 qhipaq kuskanpi',
ro='a doua jumătate a lui $date1',
ru={'вторая половина $date1', case={'gen',''}},
sk={'2. polovica $date1', case={'gen',''}},
sl={'druga polovica {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sv='andra halvan av $date1',
uk={'друга половина $date1', case={'gen',''}},
ur='نصف آخر $date1',
vi='nửa cuối năm $date1',
['zh-hans']='$date1下半期',
['zh-hant']='$date1下半期',
default='<span style="color:red">second half of $date1</span>',
},
['1quarter']= {
nDates = 1,
bn='$date1-এর প্রথম ত্রৈমাসিক',
['be-tarask']={'першая чвэрць $date1', case={'gen',''}},
cs={'1. čtvrtina $date1', case={'gen',''}},
de='erstes Viertel $date1',
en='first quarter of $date1',
['en-gb']='first quarter of $date1',
['en-ca']='first quarter of $date1',
eo='unua kvarono de $date1',
es='primer cuarto del $date1',
fa='ربع نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 ensimmäinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'premier quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון ראשון של $date1',
hsb={'prěnja štwórćina $date1', case={'gen',''}},
hr='prva četvrtina $date1',
it='primo quarto del $date1',
ja='$date1第1四半期',
ml='$date1 ഒന്നാം ത്രൈമാസം',
nb='første kvartal av $date1',
nl='eerste kwart $date1',
nn='fyrste kvartalet av $date1',
no='første kvartal av $date1',
pl={'pierwsza ćwiartka $date1', case={'gen',''}},
qu='$date1 ñawpaq ñiqin tawa phatmanpi',
ru={'первая четверть $date1', case={'gen',''}},
sk={'1. štvrtina $date1', case={'gen',''}},
sl={'prva četrtina {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sl={function(date1, state) return 'prva četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första kvartalet av $date1',
uk={'1-ша чверть $date1', case={'gen',''}},
ur='پہلی سہ ماہی $date1',
vi='quý 1 năm $date1',
['zh-hans']='$date1第一季度',
['zh-hant']='$date1第一季度',
default='<span style="color:red">first quarter of $date1</span>',
},
['2quarter']= {
nDates = 1,
['be-tarask']={'другая чвэрць $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয় ত্রৈমাসিক',
cs={'2. čtvrtina $date1', case={'gen',''}},
de='zweites Viertel $date1',
en='second quarter of $date1',
['en-gb']='second quarter of $date1',
['en-ca']='second quarter of $date1',
eo='dua kvarono de $date1',
es='segundo cuarto del $date1',
fa='ربع دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 toinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'deuxième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שני של $date1',
hsb={'druha štwórćina $date1', case={'gen',''}},
hr='druga četvrtina $date1',
it='secondo quarto del $date1',
ja='$date1第2四半期',
ml='$date1 രണ്ടാം ത്രൈമാസം',
nb='andre kvartal av $date1',
nl='tweede kwart $date1',
nn='andre kvartalet av $date1',
no='andre kvartal av $date1',
pl={'druga ćwiartka $date1', case={'gen',''}},
qu='$date1 iskay ñiqin tawa phatmanpi',
ru={'вторая четверть $date1', case={'gen',''}},
sk={'2. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'druga četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='andra kvartalet av $date1',
uk={'2-га чверть $date1', case={'gen',''}},
ur='دوسری سہ ماہی $date1',
vi='quý 2 năm $date1',
['zh-hans']='$date1第二季度',
['zh-hant']='$date1第二季度',
default='<span style="color:red">second quarter of $date1</span>',
},
['3quarter']= {
nDates = 1,
['be-tarask']={'трэцяя чвэрць $date1', case={'gen',''}},
bn='$date1-এর তৃতীয় ত্রৈমাসিক',
cs={'3. čtvrtina $date1', case={'gen',''}},
de='drittes Viertel $date1',
en='third quarter of $date1',
['en-gb']='third quarter of $date1',
['en-ca']='third quarter of $date1',
eo='tria kvarono de $date1',
es='tercer cuarto del $date1',
fa='ربع سوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 kolmas neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'troisième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שלישי של $date1',
hsb={'třeća štwórćina $date1', case={'gen',''}},
hr='treća četvrtina $date1',
it='terzo quarto del $date1',
ja='$date1第3四半期',
ml='$date1 മൂന്നാം ത്രൈമാസം',
nb='tredje kvartal av $date1',
nl='derde kwart $date1',
nn='tredje kvartalet av $date1',
no='tredje kvartal av $date1',
pl={'trzecia ćwiartka $date1', case={'gen',''}},
qu='$date1 kimsa ñiqin tawa phatmanpi',
sk={'3. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'tretja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
ru={'третья четверть $date1', case={'gen',''}},
sv='tredje kvartalet av $date1',
uk={'3-тя чверть $date1', case={'gen',''}},
ur='تیسری سہ ماہی $date1',
vi='quý 3 năm $date1',
['zh-hans']='$date1第三季度',
['zh-hant']='$date1第三季度',
default='<span style="color:red">third quarter of $date1</span>',
},
['4quarter']= {
nDates = 1,
['be-tarask']={'чацьвёртая чвэрць $date1', case={'gen',''}},
bn='$date1-এর চতুর্থ ত্রৈমাসিক',
cs={'4. čtvrtina $date1', case={'gen',''}},
de='letztes Viertel $date1',
en='fourth quarter of $date1',
['en-gb']='fourth quarter of $date1',
['en-ca']='fourth quarter of $date1',
eo='kvara kvarono de $date1',
es='último cuarto del $date1',
fa='ربع چهارم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 viimeinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'quatrième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון רביעי של $date1',
hsb={'poslednja štwórćina $date1', case={'gen',''}},
hr='četvrta četvrtina $date1',
it='quarto quarto del $date1',
ja='$date1第4四半期',
ml='$date1 നാലാം ത്രൈമാസം',
nb='fjerde kvartal av $date1',
nl='vierde kwart $date1',
nn='tredje kvartalet av $date1',
no='fjerde kvartal av $date1',
pl={'czwarta ćwiartka $date1', case={'gen',''}},
qu='$date1 qhipaq ñiqin tawa phatmanpi',
ru={'последняя четверть $date1', case={'gen',''}},
sk={'4. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'zadnja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='fjärde kvartalet av $date1',
uk={'4-та чверть $date1', case={'gen',''}},
ur='چوتھی سہ ماہی $date1',
vi='quý 4 năm $date1',
['zh-hans']='$date1第四季度',
['zh-hant']='$date1第四季度',
default='<span style="color:red">forth quarter of $date1</span>',
},
-- ==========================================
-- === Seasons ==============================
-- ==========================================
['spring'] = {
nDates = 1,
ar='ربيع $date1',
['be-tarask']='вясна $date1',
bn='বসন্তকাল $date1',
ca='primavera $date1',
cs={'jaro $date1', case={'gen', ''}},
da='Forår $date1',
de='Frühling $date1',
en='Spring $date1',
['en-gb']='Spring $date1',
['en-ca']='Spring $date1',
eo='printempo $date1',
es='primavera de $date1',
et='$date1 kevad',
fa='بهار $date1',
fi='Kevät $date1',
fr='printemps $date1',
gl='primavera $date1',
he='אביב $date1',
hr='proljeće $date1',
hsb='nalěćo $date1',
hu='$date1 tavasza',
it='primavera $date1',
ja='{{#if:$date1|$date1年}}春',
mk='пролет $date1',
ml='$date1 വസന്തം',
nb='våren $date1',
nds='Vörjohr $date1',
nn='våren $date1',
nl='voorjaar $date1',
no='våren $date1',
pl='wiosna $date1',
pt='primavera de $date1',
ro='primăvara lui $date1',
ru='весна $date1',
sk={'jar $date1', case={'gen', ''}},
sl='pomlad $date1',
sv='våren $date1',
uk='весна $date1',
ur='بہار $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}春季',
['zh-hant']='{{#if:$date1|$date1年}}春季',
default='<span style="color:red">Spring $date1</span>',
},
['summer'] = {
nDates = 1,
ar='صيف $date1',
['be-tarask']='лета $date1',
bn='গ্রীষ্মকাল $date1',
ca='estiu $date1',
cs={'léto $date1', case={'gen', ''}},
da='Sommer $date1',
de='Sommer $date1',
en='Summer $date1',
['en-gb']='Summer $date1',
['en-ca']='Summer $date1',
eo='somero $date1',
es='verano de $date1',
et='$date1 suvi',
fa='تابستان $date1',
fi='Kesä $date1',
fr='été $date1',
gl='verán $date1',
he='קיץ $date1',
hr='ljeto $date1',
hsb='lěćo $date1',
hu='$date1 nyara',
it='estate $date1',
ja='{{#if:$date1|$date1年}}夏',
mk='лето $date1',
ml='$date1 വേനൽ',
nb='sommeren $date1',
nds='Sommer $date1',
nn='sumaren $date1',
nl='zomer $date1',
no='sommeren $date1',
pl='lato $date1',
pt='verão de $date1',
ro='vara lui $date1',
ru='лето $date1',
sk={'leto $date1', case={'gen', ''}},
sl='poletje $date1',
sv='sommaren $date1',
uk='літо $date1',
ur='گرما $date1',
vi='mùa hè năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}夏季',
['zh-hant']='{{#if:$date1|$date1年}}夏季',
default='<span style="color:red">Summer $date1</span>',
},
['autumn'] = {
nDates = 1,
ar='خريف $date1',
['be-tarask']='восень $date1',
bn='শরত্কাল $date1',
ca='tardor $date1',
cs={'podzim $date1', case={'gen', ''}},
da='Efterår $date1',
de='Herbst $date1',
en='Autumn $date1',
['en-gb']='Autumn $date1',
['en-ca']='Autumn $date1',
eo='aŭtuno $date1',
es='otoño de $date1',
et='$date1 sügis',
fa='پاییز $date1',
fi='Syksy $date1',
fr='automne $date1',
gl='outono $date1',
he='סתיו $date1',
hr='jesen $date1',
hsb='nazyma $date1',
hu='$date1 ősze',
it='autunno $date1',
ja='{{#if:$date1|$date1年}}秋',
mk='есен $date1',
ml='$date1 ശരത്കാലം',
nb='høsten $date1',
nds='Harvst $date1',
nn='hausten $date1',
nl='herfst $date1',
no='høsten $date1',
pl='jesień $date1',
pt='outono de $date1',
ro='toamna lui $date1',
ru='осень $date1',
sk={'jeseň $date1', case={'gen', ''}},
sl='jesen $date1',
sv='hösten $date1',
uk='осінь $date1',
ur='خزاں $date1',
vi='mùa thu năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}秋季',
['zh-hant']='{{#if:$date1|$date1年}}秋季',
default='<span style="color:red">Autumn $date1</span>',
},
['winter'] = {
nDates = 1,
ar='شتاء $date1',
['be-tarask']='зіма $date1',
bn='শীতকাল $date1',
ca='hivern $date1',
cs={'zima $date1', case={'gen', ''}},
da='Vinter $date1',
de='Winter $date1',
en='Winter $date1',
['en-gb']='Winter $date1',
['en-ca']='Winter $date1',
eo='vintro $date1',
es='invierno de $date1',
et='$date1 talv',
fa='زمستان $date1',
fi='Talvi $date1',
fr='hiver $date1',
gl='inverno $date1',
he='חורף $date1',
hr='zima $date1',
hsb='zyma $date1',
hu='$date1 tele',
it='inverno $date1',
ja='{{#if:$date1|$date1年}}冬',
mk='зима $date1',
ml='$date1 ശൈത്യകാലം',
nds='Winter $date1',
nl='winter $date1',
nb='vinteren $date1',
nn='vinteren $date1',
no='vinteren $date1',
pl='zima $date1',
pt='inverno de $date1',
ro='iarna lui $date1',
ru='зима $date1',
sk={'zima $date1', case={'gen', ''}},
sl='zima $date1',
sv='vintern $date1',
uk='зима $date1',
ur='سرما $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}冬季',
['zh-hant']='{{#if:$date1|$date1年}}冬季',
default='<span style="color:red">Winter $date1</span>',
},
-- ==========================================
-- === Decade/Century/Millennium ============
-- ==========================================
['decade'] = {
nDates = 1,
ar='أعوام $date1',
['be-tarask']='$date1-я гады',
bn='$date1-এর দশক',
ca='dècada del $date1',
cs=function(date1, state) return ((date1 % 100 == 0) and 'první dekáda' or ((date1 % 100) .. '. léta')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. století' end,
da="$date1'erne",
de='$date1er-Jahre',
el='δεκαετία του $date1',
en='$date1s',
['en-gb']='$date1s',
['en-ca']='$date1s',
eo='$date1-aj jaroj',
es='años $date1',
et='$date1ndad',
fa='دههٔ $date1',
fi='$date1-luvulla',
fr='années $date1',
gl='anos $date1',
he='שנות ה-$date1',
hr='$date1-ih',
hsb='$date1te lěta',
hu='$date1-s évek',
it="{{#ifexpr: $date1 mod 100 = 0 | primo decennio del {{Roman|{{#expr: ($date1 / 100) + 1}}}} sec. | anni '{{#expr: $date1 mod 100 }} del {{Roman|{{#expr: floor($date1 / 100) + 1}}}} sec.}}",
ja='$date1年代',
mk='$date1-ти',
ml='ദശകം $date1-ൽ',
nb='$date1-årene',
nn='$date1-åra',
nds='Johren $date1',
nl='jaren $date1',
no='$date1-årene',
pl=function(date1, state)
local str = 'lata '..(date1 % 100)..'. '
if (date1 % 100 == 0) then
str = 'pierwsza dekada '
elseif (date1 % 100 == 10) then
str = 'druga dekada '
end
return str .. Roman(math.floor(date1 / 100) + 1) .. ' wieku'
end,
pt='década de $date1',
qu='$date1 watakuna',
ro='anii $date1',
ru='$date1-е годы',
sk=function(date1, state) return ((date1 % 100 == 0) and 'prvé desaťročie' or ((date1 % 100) .. '. roky')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. storočia' end,
sl='$date1-ta leta',
sv='$date1-talet{{#switch:$date1|1300|1400|1500|1600|1700|1800|1900|2000|2100= (decennium)}}',
uk='$date1-ті роки',
ur='دہائی $date1',
vi='thập niên $date1',
['zh-hans']='$date1年代',
['zh-hant']='$date1年代',
default='<span style="color:red">$date1s</span>',
},
['century'] = {
nDates = 1,
ar='القرن $date1',
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' стагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' শতাব্দী' end,
ca='segle {{small-caps|{{lc:{{Roman year|$date1}}}}}}',
cs='$date1. století',
da='$date1. århundrede',
de='$date1. Jahrhundert',
el='$date1ος αιώνας',
en=function(date1, state) return Ordinal(date1, 'en') .. ' century' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' century' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' century' end,
eo='$date1-a jarcento',
es=function(date1, state) return 'siglo ' .. Roman(date1) end,
et='$date1. sajand',
fa='سدهٔ $date1',
fi='{{#expr: ($date1 - 1) * 100}}-luvulla',
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle' end,
gl=function(date1, state) return 'século ' .. Roman(date1) end,
he='המאה ה-$date1',
hr='$date1. stoljeće',
hsb='$date1. lětstotk',
hu='$date1. évszázad',
it=function(date1, state) return Roman(date1) .. ' sec.' end,
ja='$date1世紀',
la='saeculum $date1',
mk=function(date1, state) return Roman(date1) .. ' век' end,
ml='$date1-ആം നൂറ്റാണ്ട്',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' århundre' end,
nn=function(date1, state) return 'det ' .. Ordinal(date1, 'no') .. ' århundre' end,
nds='$date1. Johrhunnert',
nl='$date1e eeuw',
no=function(date1, state) return Ordinal(date1, 'no') .. ' århundre' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decCentury('pl',state.case) end,
pt=function(date1, state) return 'século ' .. Roman(date1) end,
qu='$date1 ñiqin pachakwata',
ro='secolul al $date1-lea',
ru=function(date1, state) return Roman(date1) .. ' век' end,
sk='$date1. storočie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. stoletje' end,
sv='{{#expr:$date1 - 1}}00-talet',
tt=function(date1, state) return Roman(date1) .. ' гасыр' end,
uk=function(date1, state) return Roman(date1) .. ' ст.' end,
ur='صدی $date1',
vi='thế kỷ $date1',
['zh-hans']='$date1世纪',
['zh-hant']='$date1世紀',
default='<span style="color:red">$date1 century</span>',
},
['millennium'] = {
nDates = 1,
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' тысячагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' সহস্রাব্দ' end,
cs='$date1. tisíciletí',
de='$date1. Jahrtausend',
en=function(date1, state) return Ordinal(date1, 'en') .. ' millennium' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' millennium' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' millennium' end,
eo='$date1-a jarmilo',
es=function(date1, state) return Roman(date1) .. ' milenio' end,
fa=function(date1, state) return 'هزارهٔ ' .. Ordinal(date1, 'fa') end,
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' millénaire' end,
hr='$date1. tisućljeće',
hsb='$date1. lěttysac',
it=function(date1, state) return Roman(date1) .. ' millennio' end,
ja='$date1千年紀',
la='millennium $date1',
ml='$date1-മത്തെ സഹസ്രാബ്ദം',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' årtusen' end,
nn=function(date1, state) return Ordinal(date1, 'no') .. ' tusenår' end,
nl='$date1e millennium',
no=function(date1, state) return Ordinal(date1, 'no') .. ' årtusen' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decMillennium('pl',state.case) end,
pt=function(date1, state) return Roman(date1) .. ' milénio' end,
qu='$date1 ñiqin waranqawata',
ru=function(date1, state) return Roman(date1) .. ' тысячелетие' end,
sk='$date1. tisícročie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. tisočletje' end,
sv=function(date1, state) return Ordinal(date1, 'sv') .. ' årtusendet' end,
uk=function(date1, state) return Roman(date1) .. ' тис.' end,
ur=function(date1, state) return 'ہزارہ ' .. Ordinal(date1, 'ur') end,
vi='thiên niên kỷ $date1',
['zh-hans']=function(date1, state) return Ordinal(date1, 'zh-hans') .. '千年' end,
['zh-hant']=function(date1, state) return Ordinal(date1, 'zh-hant') .. '千年' end,
--default='{{Please translate|$date1 millennium|Module:I18n/complex date}}',
default='<span style="color:red">$date1 millennium</span>',
},
['million'] = {
de = '$date1 Millionen Jahre',
en = '$date1 million years',
['en-gb'] = '$date1 million years',
['en-ca'] = '$date1 million years',
eo = '$date1 milionoj da jaroj',
es = '$date1 millones de años',
nl = '$date1e miljoen jaar',
fr = '$date1 millions d’années',
it = '$date1 milioni di anni',
ml = function(date1, state)
if date1 >= 10 then
return (date1 / 10) .. ' കോടി വർഷങ്ങൾ' -- 100 lakh = 1 crore
else
return (date1 * 10) .. ' ലക്ഷം വർഷങ്ങൾ'
end
end,
nb = '$date1 millioner år',
nl = '$date1 miljoen jaar',
nn = '$date1 millionar år',
pl = '$date1 milionów lat',
pt = '$date1 milhões de anos',
ru = '$date1 миллиона лет',
uk = '$date1 мільйони років',
ur = '$date1 ملین سال',
['zh-hans'] = '$date1百万年',
['zh-hant'] = '$date1百万年',
default='<span style="color:red">$date1 million years</span>',
nDates = 1,
},
['billion'] = {
de = '$date1 Milliarden Jahre',
en = '$date1 billion years',
['en-gb'] = '$date1 billion years',
['en-ca'] = '$date1 billion years',
es = '$date1 mil millones de años',
nl = '$date1e miljard jaar',
fr = '$date1 milliard d’années',
it = '$date1 miliardi di anni',
ml = function(date1, state) return (date1 * 10) .. ' കോടി വർഷങ്ങൾ' end,
nb = '$date1 milliarder år',
nl = '$date1 miljard jaar',
nn = '$date1 milliardar år',
pl = '$date1 miliardów lat',
pt = '$date1 mil milhões de anos',
ru = '$date1 миллиардов лет',
uk = '$date1 мільярдів років',
ur = '$date1 بلین سال',
['zh-hans'] = '$date1十亿年',
['zh-hant'] = '$date1十億年',
default='<span style="color:red$date1 billion years</span>',
nDates = 1,
},
-- ==========================================
-- === Complex phrases ======================
-- ==========================================
-- Phrases which for some languages can be build out of simpler phrases
-- Their "default" state, used when no translation in the requested language was found, returns a "state" like
-- table which is used to call the function again and built the phrase out of simple phrases
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the century'] = {
nDates = 2,
['be-tarask']=function(date1, date2, state) return 'канец ' .. Roman(date1) ..' — пачатак ' .. Roman(date2) ..' стагодзьдзя' end,
bn=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'bn') .. ' শতাব্দীর পালাবদলে' end,
cs='přelom $date1. a $date2. století',
de=function(date1, date2, state) return 'Jahrhundertwende $date1/' .. Ordinal(date2, 'de') end,
en=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en') .. ' century' end,
['en-gb']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-gb') .. ' century' end,
['en-ca']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-ca') .. ' century' end,
eo='ŝanĝo de la $date1-a al la $date2-a jarcentoj',
es=function(date1, date2, state) return 'hacia finales del siglo ' .. Roman(date1) .. ' y principios del siglo ' .. Roman(date2) .. '' end,
fi='{{#expr: $date1 * 100-100}}-luvun loppu ja {{#expr: $date2 * 100-100}}-luvun alku',
fr=function(date1, date2, state) return 'fin du ' .. Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle / début du ' .. Ordinal(date2, 'fr', 'roman', 'm') end,
hr='kraj $date1. i početak $date2. stoljeća',
hsb=function(date1, date2, state) return 'přechod ' .. Roman(date1) ..' do ' .. Roman(date2) ..' lětstotka' end,
it=function(date1, date2, state) return 'fine del ' .. Roman(date1) .. ' sec. e inizio del ' .. Roman(date2) .. ' sec.' end,
ja='$date1世紀から$date2世紀への変わり目',
ml='$date1 നൂറ്റാണ്ട് അവസാനവും $date2 നൂറ്റാണ്ട് ആദ്യവും',
nb=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nb') .. ' århundre/begynnelsen av det ' .. Ordinal(date2, 'nb') .. ' århundre' end,
nl=function(date1, date2, state) return 'eind $date1e/begin $date2e eeuw' end,
nn=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nn') .. ' hundreåret/byrjinga av det ' .. Ordinal(date2, 'nn') .. ' hundreåret' end,
pl=function(date1, date2, state) return 'przełom ' .. Roman(date1) ..' i ' .. Roman(date2) ..' wieku' end,
qu="$date1 ñiqin waranqawatamanta $date2 ñiqin waranqawataman t'ikrasqanpi",
ru=function(date1, date2, state) return 'конец ' .. Roman(date1) ..' — начало ' .. Roman(date2) ..' века' end,
sk='prelom $date1. a $date2. storočia',
sl=function(date1, date2, state) return 'pozno ' .. Ordinal(date1, 'sl') .. '. in zgodnje ' .. Ordinal(date2, 'sl') .. '. stoletje' end,
sv=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'sv') .. ' årtusendets sekelskifte' end,
uk=function(date1, date2, state) return 'кін. ' .. Roman(date1) ..' ст. — поч. ' .. Roman(date2) ..' ст.' end,
vi='bước sang thế kỷ $date2',
['zh-hans']=function(date1, date2, state) return '$date1世纪末' .. Ordinal(date2, 'zh-hans') .. '世纪初' end,
['zh-hant']=function(date1, date2, state) return '$date1世紀末' .. Ordinal(date2, 'zh-hant') .. '世紀初' end,
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='century', era1=state.era1, adj2 = 'early', unit2='century', era2=state.era2} end,
},
-- $date1 - original date - 10
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the decade']={
nDates = 2,
['be-tarask']='канец $date1-х — пачатак $date2-х гг.',
bn='$date1-এর দশকের শেষে এবং $date2-এর দশকের শুরুতে',
cs=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "přelom 1. a 2. dekády " .. century1 .. ". století"
elseif decade2 == 0 and date2 < date1 then
return "přelom 9. a 10. dekády " .. century1 .. ". století"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "přelom " .. century1 .. ". a " .. century2 .. ". století"
else
return "přelom " .. decade1 .. "0. a " .. decade2 .. "0. let " .. century1 .. ". století"
end
end,
de='späte $date1er und frühe $date2er',
en='late $date1s and early $date2s',
['en-gb']='late $date1s and early $date2s',
['en-ca']='late $date1s and early $date2s',
eo='ŝanĝo de la $date1-a al la $date2-a jardekoj',
es='finales de los años $date1 y principios de los años $date2',
fa='اواخر دههٔ $date1 و اوایل دههٔ $date2',
fi='$date1-luvun loppu ja $date2-luvun alku',
fr='fin des années $date1 / début des années $date2',
hr='kraj $date1-ih i početak $date2-ih',
hsb='pózdnje $date1te a zažne $date2te lěta',
it='fine degli anni $date1 / inizio degli anni $date2',
ja='$date1年代末から$date2年代初頭',
ml='$date1കൾ അവസാനവും $date2കൾ ആദ്യവും',
nb = 'sent på $date1-tallet og tidlig på $date2-tallet',
nl = 'eind $date1e/begin $date2e decennium',
nn = 'seint på $date1-talet og tidleg på $date2-talet',
pl=function(date1, date2, state)
local decade1 = math.floor(date1 % 100)
local decade2 = math.floor(date2 % 100)
local century1 = Roman(math.floor( date1 / 100) + 1)
local century2 = Roman(math.floor( date2 / 100) + 1)
if decade1 == 0 and date1 < date2 then
return "przełom pierwszej i drugiej dekady " .. century1 .. " wieku"
elseif decade1 == 10 and date1 < date2 then
return "przełom drugiej dekady i lat 20. " .. century1 .. " wieku"
elseif decade1 == 90 and decade2 == 0 then
return "przełom ostatniej dekady " .. century1 .. " i pierwszej dekady " .. century2 .. " wieku"
else
return "przełom lat " .. decade1 .. ". i " .. decade2 .. ". " .. century1 .. " wieku"
end
end,
qu='qhipaq $date1 watakuna ñawpaq $date2 watakunapas',
ru='конец $date1-х — начало $date2-х гг.',
sv='sena $date1-talet och tidiga $date2-talet',
sk=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "prelom 1. a 2. desaťročia " .. century1 .. ". storočia"
elseif decade2 == 0 and date2 < date1 then
return "prelom 9. a 10. desaťročia " .. century1 .. ". storočia"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "prelom " .. century1 .. ". a " .. century2 .. ". storočia"
else
return "prelom " .. decade1 .. "0. a " .. decade2 .. "0. rokov " .. century1 .. ". storočia"
end
end,
sl='pozna $date1-ta in zgodnja $date2-ta leta',
uk='кінець $date1-х — початок $date2-х рр.',
ur='اخیر دہائی $date1 و ابتدا دہائی $date2',
vi='bước sang thập niên $date2',
['zh-hans']='$date1年代末$date2年代初',
['zh-hant']='$date1年代末$date2年代初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='decade', era1=state.era1, adj2 = 'early', unit2='decade', era2=state.era2} end,
},
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the year']={
nDates = 2,
['be-tarask']='канец $date1 — пачатак $date2 г.',
bn='$date1-এর শেষে এবং $date2-এর শুরুতে',
cs='přelom roku $date1/$date2',
de='Jahreswechsel $date1/$date2',
en='late $date1 and early $date2',
['en-gb']='late $date1 and early $date2',
['en-ca']='late $date1 and early $date2',
eo='jarŝanĝo $date1 / $date2',
es='finales de $date1 y principios de $date2',
fa='اواخر $date1 و اوایل $date2',
fi='vuoden $date1 loppu ja vuoden $date2 alku',
fr='fin $date1 / début $date2',
hsb='kónc $date1/spočatk $date2',
ja='$date1年末から$date2年初め',
ml='$date1 അവസാനവും $date2 ആദ്യവും',
nb='årsskiftet $date1/$date2',
nl='eind $date1 en begin $date2',
nn='årsskiftet $date1/$date2',
pl='koniec $date1 i początek $date2 roku',
qu="$date1 qhipaq p'unchawkuna $date2 ñawpaq p'unchawkunapas",
ru='конец $date1 — начало $date2 г.',
sk='prelom rokov $date1/$date2',
sl='konec leta $date1 in začetek $date2',
sv='sena $date1 och tidiga $date2',
uk='кінець $date1 — початок $date2 р.',
ur='اخیر $date1 و ابتدا $date2',
vi='bước sang năm $date2',
['zh-hans']='$date1年末$date2年初',
['zh-hant']='$date1年末$date2年初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='year', era1=state.era1, adj2 = 'early', unit2='year', era2=state.era2} end,
},
['circa2'] = {
nDates = 2,
ar='حوالي $date1 – $date2',
['be-tarask']={'прыкладна з $date1 да прыкладна $date2', case={'gen','gen'}},
bn='প্রায় $date1 এবং প্রায় $date2-এর মাঝামাঝি',
ca='cap a $date1 o $date2',
cs='cca $date1 až $date2',
da='ca. mellem $date1 og $date2',
de='etwa $date1 bis $date2',
el='περίπου μεταξύ $date1 και $date2',
en='between circa $date1 and circa $date2',
['en-gb']='between circa $date1 and circa $date2',
['en-ca']='between circa $date1 and circa $date2',
eo='proksimume de $date1 ĝis $date2',
es='hacia $date1 o $date2',
et='ca. $date1 ja $date2 vahel',
fa='حدوداً بین $date1 و $date2',
fi={function(date1, date2, state) return 'suunnilleen ' .. decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='vers $date1-$date2',
gl='entre $date1 e $date2',
he='בסביבות ה- $date1 עד $date2',
hr='oko između $date1 i $date2',
hsb={'wot něhdźe $date1 do něhdźe $date2', case={'gen','gen'}},
hu='$date1 és $date2 között',
it='tra il $date1 e il $date2 circa',
ja='$date1頃と$date2頃の間',
mk='~ помеѓу $date1 и $date2',
ml='ഉദ്ദേശം $date1-നും ഉദ്ദേശം $date2-നും ഇടയ്ക്ക്',
nb='ca. mellom $date1 og $date2',
nn='ca. mellom $date1 og $date2',
nds='üm un bi $date1 bet $date2',
nl='ca. $date1 tot $date2',
no='ca. mellom $date1 og $date2',
pl={'od około $date1 do około $date2', case={'gen','gen'}},
pt='cerca de $date1 a $date2',
qu="$date1 p'unchawmantachá $date2 p'unchawkamachá",
ro='circa $date1 – $date2',
ru={'с около $date1 до, примерно, $date2', case={'gen','gen'}},
sk='cca $date1 až $date2',
sl={function(date1, date2, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1 do $date2' end, case={'gen','gen'}},
sv='c:a $date1 till $date2',
uk={'з близько $date1 до, приблизно, $date2', case={'gen','gen'}},
ur='قریباً بین $date1 و $date2',
vi='giữa vào khoảng $date1 và vào khoảng $date2',
['zh-hans']='约$date1~$date2间',
['zh-hant']='約$date1~$date2間',
default=function(date1, date2, state) return {conj='between', adj1 = 'circa', unit1=state.unit1, era1=state.era1, adj2 = 'circa', unit2=state.unit2, era2=state.era2} end,
},
-- ==========================================
-- === Eras and calendars ===================
-- ==========================================
['julian'] = {
nDates = 2,
ar='$date1 ($date2 حسب [[w:ar:التقويم اليولياني|تقويم يولياني]])',
['be-tarask']='$date1 ($date2 паводле [[w:be-tarask:Юліянскі каляндар|ст. ст.]]',
bn='$date1 (জুলিয়ান ক্যালেন্ডারে $date2)',
ca='$date1 ($date2 segons el [[w:ca:Calendari julià|calendari julià]])',
cs='$date1 ($date2 podle [[w:cs:Juliánský kalendář|juliánského kalendáře]])',
da='$date1 ($date2 [[w:da:Julianske kalender|Julianske kalender]])',
de='$date1 ($date2 [[w:de:Julianischer Kalender|Julianischer Kalender]])',
el='$date1 ($date2 με το [[w:el:Ιουλιανό ημερολόγιο|Ιουλιανό ημερολόγιο]])',
en='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-gb']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-ca']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
fa='$date1 ($date2 در [[w:fa:Old Style and New Style dates|تقویم جولیان]])',
eo='$date1 ($date2 laŭ la [[w:eo:Julia kalendaro|Julia Kalendaro]])',
es='$date1 ($date2 según el [[w:es:Calendario juliano|calendario juliano]])',
et='$date1 ($date2 [[w:et:Juliuse kalender|Juliuse kalendris]])',
fi='$date1 ($date2 [[w:fi:juliaaninen kalenteri|juliaanisen kalenterin]] mukaan)',
fr='$date1 ($date2 dans le [[w:fr:calendrier julien|calendrier julien]])',
gl='$date1 ($date2 segundo o [[w:gl:Calendario xuliano|calendario xuliano]])',
hu='$date1 ($date2 a [[w:hu:Julianus-naptár|Julián naptárban]])',
hr='$date1 ($date2 prema [[w:hr:Julijanski kalendar|julijanskom kalendaru]])',
he='$date1 ($date2 ב[[w:he:לוח היוליאני|הלוח היוליאני]])',
hsb='$date1 ($date2 po julianiskej protyce)',
it='$date1 ($date2 secondo il [[w:it:Calendario giuliano|calendario giuliano]])',
ja='$date1([[w:ja:ユリウス暦|ユリウス暦]] $date2)',
mk='$date1 ($date2 по [[w:mk:Јулијански календар|Јулијанскиот календар]])',
ml='$date1 ([[w:ml:ജൂലിയൻ കാലഗണനാരീതി|ജൂലിയൻ വർഷത്തിൽ]] $date2)',
ml='$date1 ([[w:Old Style and New Style dates|ജൂലിയൻ കാലഗണനാരീതിയിൽ]] $date2)',
nds='$date1 ($date2 na’n [[w:nds:Juliaansch Klenner|Juliaanschen Klenner]])',
nb='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nn='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nl='$date1 ($date2 volgens de [[w:nl:Juliaanse kalender|Juliaanse kalender]])',
no='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalender]])',
pl='$date1 ($date2 wedle [[w:pl:Daty nowego i starego porządku|kalendarza juliańskiego]])',
pt='$date1 ($date2 no [[w:pt:Mudança para o calendário gregoriano|calendário juliano]])',
qu='$date1 ($date2 [[w:qu:Hulyanu kalindaryu|Hulyanu kalindaryupi]])',
ro='$date1 ($date2 în [[w:ro:calendarul iulian|calendarul iulian]])',
ru='$date1 ($date2 [[w:ru:Юлианский календарь|по ст. ст.]])',
sk='$date1 ($date2 podľa [[w:sk:Juliánsky kalendár|juliánskeho kalendára]])',
sl='$date1 ($date2 po [[w:sl:Julijanski koledar|julijanskem koledarju]])',
sv='$date1 ($date2 [[w:sv:Gamla stilen|gamla stilen]])',
uk='$date1 ($date2 [[w:uk:Юліанський календар|Юліанський календар]])',
ur='$date1 ($date2 حسب [[w:ur:نئے اور پانے انداز کی تاریخ|جولین تقویم]])',
vi='$date1 ($date2 trong [[w:vi:Lịch Julius|lịch Juliêng]])',
['zh-hans']='$date1([[w:Old Style and New Style dates|格里历]]$date2)',
['zh-hant']='$date1([[w:Old Style and New Style dates|格里曆]]$date2)',
default='<span style="color:red">$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])</span>',
},
['ad'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:ад Нараджэньня Хрыста|пасьля Н. Х.]]',
bn ='$date1 খ্রিস্টাব্দ',
ca ='$date1 dC',
cs ='$date1 n. l.',
de ='$date1 n. Chr.',
el ='$date1 μ.Χ.',
en ='$date1 [[w:en:Anno Domini|AD]]',
['en-gb'] ='$date1 [[w:en:Anno Domini|AD]]',
['en-ca'] ='$date1 [[w:en:Anno Domini|AD]]',
eo ='$date1 pK',
es ='$date1 [[w:es:Anno Dómini|d. C.]]',
fa ='$date1 م.',
fi ='$date1 jaa.',
fr ='$date1 ap. J.-C.',
gl ='$date1 d.C.',
he ='$date1 לספירה',
hu ='i. sz. $date1',
hr ='$date1 p. Kr.',
hsb='$date1 po Chr.',
it ='$date1 d.C.',
ja ='西暦$date1',
mk ='$date1 н.е.',
ml ='ക്രി.വ. $date1',
nb ='$date1 e.Kr.',
nl ='$date1 n.Chr.',
nn ='$date1 e.Kr.',
no ='$date1 e.Kr.',
pl ='$date1 [[w:pl:naszej ery|n.e.]]',
ro ='$date1 d.Hr.',
ru ='$date1 н. э.',
sk ='$date1 po Kr.',
sl ='$date1 n. št.',
sv ='$date1 [[w:sv:Efter Kristus|e.Kr.]]',
uk ='$date1 н. е.',
ur ='$date1 ء',
vi ='$date1 [[w:vi:Công Nguyên|CN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元]]$date1',
default='<span style="color:red">$date1 [[w:en:Anno Domini|AD]]</span>',
},
['ah'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:Ісламскі каляндар|хіджры]]',
bn ='$date1 [[w:bn:ইসলামি বর্ষপঞ্জি|হিজরী]]',
cs ='$date1 [[w:cs:Anno Hegirae|AH]]',
en ='$date1 [[w:en:Hijri year|AH]]',
['en-gb'] ='$date1 [[w:en:Hijri year|AH]]',
['en-ca'] ='$date1 [[w:en:Hijri year|AH]]',
eo ='$date1 post la [[w:eo:Heĝiro|Heĝiro]]',
fa ='$date1 ه.ق.',
fr ='$date1 [[w:fr:Calendrier musulman|A.H.]]',
he ="$date1 [[w:he:הלוח המוסלמי|להג'רה]]",
hr ='$date1 god. hidžre',
ja ='[[w:ja:ヒジュラ暦|ヒジュラ暦]]$date1',
ml ='$date1 [[w:ml:ഇസ്ലാമിക കലണ്ടർ|ഹിജ്റ]]',
nb ='$date1 [[w:no:Den muslimske kalenderen|A.H.]]',
nn ='$date1 [[w:nn:Den muslimske kalenderen|A.H.]]',
pl ='$date1 [[w:pl:Kalendarz muzułmański|A.H.]]',
ru ='$date1 [[w:ru:Исламский календарь|хиджры]]',
sk ='$date1 [[w:sk:A. H.|A. H.]]',
sv ='$date1 [[w:sv:Muslimska kalendern|A.H.]]',
uk ='$date1 [[w:uk:Мусульманський календар|хіджри]]',
ur ='$date1 ھ',
vi ='$date1 [[w:vi:Lịch Hồi giáo|AH]]',
['zh-hans'] ='[[w:en:Hijri year|Hijri历]]$date1',
['zh-hant'] ='[[w:en:Hijri year|Hijri曆]]$date1',
default='<span style="color:red">$date1 [[w:en:Hijri year|AH]]</span>',
},
['bc'] = {
nDates = 1,
ar ='$date1 ق.م.',
['be-tarask']='$date1 [[w:be-tarask:да Нараджэньня Хрыста|да Н. Х.]]',
bn ='খ্রিস্টপূর্ব $date1',
ca ='$date1 [[w:ca:Abans de Crist|aC]]',
cs ='$date1 [[w:cs:Př. n. l.|př. n. l.]]',
de ='$date1 [[w:de:v. Chr.|v. Chr.]]',
el ='$date1 π.Χ.',
en ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-gb'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-ca'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
eo ='$date1 aK',
es ='$date1 [[w:es:Antes de Cristo|a. C.]]',
fa ='$date1 پ.م.',
fi ='$date1 eaa.',
fr ='$date1 av. J.-C.',
gl ='$date1 [[w:gl:Antes de Cristo|a.C.]]',
he ='$date1 לפני הספירה',
hr ='$date1 [[w:hr:Pr. Kr.|pr. Kr.]]',
hsb='$date1 do Chr.',
hu ='i. e. $date1',
it ='$date1 [[w:it:Avanti Cristo|a.C.]]',
ja ='[[w:ja:紀元前|紀元前]]$date1',
mk ='$date1 п.н.е.',
ml ='ക്രി.മു. $date1',
nds='$date1 vör uns Tied',
nb ='$date1 f.Kr.',
nl ='$date1 v.Chr.',
nn ='$date1 f.Kr.',
no ='$date1 f.Kr.',
pl ='$date1 [[w:pl:przed naszą erą|p.n.e.]]',
ro ='$date1 î.Hr.',
ru ='$date1 до н. э.',
sk ='$date1 [[w:sk:Pred Kristom|pred Kr.]]',
sl ='$date1 pr. n. št.',
sv ='$date1 [[w:sv:Före Kristus|f.Kr.]]',
uk ='$date1 до н. е.',
ur ='$date1 ق م',
vi ='$date1 [[w:vi:Trước Công Nguyên|TCN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元前]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元前]]$date1',
default='<span style="color:red">$date1 [[w:en:Ante Christum Natum|BC]]</span>',
},
['bp'] = {
nDates = 1,
ar ='$date1 [[w:ar:قبل الحاضر|قبل الحاضر]]',
be ='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
['be-tarask']='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
bn ='বর্তমানের আগে $date1',
ca ='$date1 [[w:ca:BP (unitat de temps)|BP]]',
cs ='$date1 [[w:cs:B. P.|B. P.]]',
de ='$date1 [[w:de:Before Present|BP]]',
el ='$date1 [[w:en:Before Present|BP]]',
en ='$date1 [[w:en:Before Present|BP]]',
['en-gb'] ='$date1 [[w:en:Before Present|BP]]',
['en-ca'] ='$date1 [[w:en:Before Present|BP]]',
eo='$date1 [[w:eo:Before Present|BP]]',
es ='$date1 [[w:es:Before Present|BP]]',
et ='$date1 [[w:et:Before Present|BP]]',
fa ='$date1 پیش از اکنون',
fi ='$date1 [[w:fi:BP (ajanlasku)|BP]]',
fr ='$date1 [[w:fr:Avant le présent|avant le présent]]',
hr ='$date1 [[w:hr:BP|prije današnjice]]',
it ='$date1 [[w:it:Before Present|BP]]',
ja ='$date1 [[w:ja:BP (年代測定)|BP]]',
lv ='$date1 [[w:lv:Pirms mūsdienām|Pirms mūsdienām]]',
mk ='$date1 [[w:mk:Пред сегашноста|п.с.]]',
ml ='[[w:en:Before Present|ഇന്നത്തെ കാലത്തിനു മുമ്പ്]] $date1',
nb ='$date1 [[w:no:Before Present|BP]]',
nn ='$date1 [[w:nn:Before Present|BP]]',
nl ='$date1 [[w:nl:Before Present|BP]]',
no ='$date1 [[w:no:Before Present|BP]]',
pl ='$date1 [[w:pl:Before Present|BP]]',
pt ='$date1 [[w:pt:Antes do Presente|Antes do Presente]]',
ru ='$date1 [[w:ru:До настоящего времени|до н. в.]]',
sk ='$date1 [[w:sk:BP (čas)|BP]]',
sl ='$date1 [[w:sl:pred sedanjostjo|p. s.]]',
sr ='$date1 [[w:sr:Прије садашњости|Прије садашњости]]',
sv ='$date1 [[w:sv:B.P.|B.P.]]',
uk ='$date1 [[w:uk:Років тому|Років тому]]',
ur ='$date1 قبل حاضر',
vi ='$date1 [[w:en:Before Present|trước ngày nay]]',
['zh-hans'] ='[[w:zh:距今|距今]]$date1',
['zh-hant'] ='[[w:zh:距今|距今]]$date1',
default='<span style="color:red">$date1 [[w:en:Before Present|BP]]</span>',
},
}
-- List of possible string inputs to "adj", "junction", "era", and "precision" fields
-- Each term is matched to its synonym and the role: a - adjective, e - era
-- p - precision, c - calendar and combos, r - certainty and j - conjunction
p.Synonyms = {
-- adjectives and prepositions
['quarter1']={'1quarter','a'},
['1stquarter']={'1quarter','a'},
['1st quarter']={'1quarter','a'},
['first quarter']={'1quarter','a'},
['1quarter']={'1quarter','a'},
['quarter2']={'2quarter','a'},
['2ndquarter']={'2quarter','a'},
['2nd quarter']={'2quarter','a'},
['second quarter']={'2quarter','a'},
['2quarter']={'2quarter','a'},
['quarter3']={'3quarter','a'},
['3rdquarter']={'3quarter','a'},
['3rd quarter']={'3quarter','a'},
['third quarter']={'3quarter','a'},
['3quarter']={'3quarter','a'},
['quarter4']={'4quarter','a'},
['4thquarter']={'4quarter','a'},
['4th quarter']={'4quarter','a'},
['fourth quarter']={'4quarter','a'},
['4quarter']={'4quarter','a'},
['spring']={'spring','a'},
['summer']={'summer','a'},
['fall']={'autumn','a'},
['autumn']={'autumn','a'},
['winter']={'winter','a'},
['beginning']={'early','a'},
['early']={'early','a'},
['1half']={'firsthalf','a'},
['1sthalf']={'firsthalf','a'},
['1st half']={'firsthalf','a'},
['firsthalf']={'firsthalf','a'},
['first half']={'firsthalf','a'},
['end']={'late','a'},
['late']={'late','a'},
['middle']={'mid','a'},
['mid']={'mid','a'},
['2half']={'secondhalf','a'},
['2ndhalf']={'secondhalf','a'},
['2nd half']={'secondhalf','a'},
['secondhalf']={'secondhalf','a'},
['second half']={'secondhalf','a'},
['<']={'before','a'},
['b']={'before','a'},
['before']={'before','a'},
['>']={'after','a'},
['a']={'after','a'},
['after']={'after','a'},
['~']={'circa','a'},
['c']={'circa','a'},
['ca']={'circa','a'},
['circa']={'circa','a'},
['by']={'by','a'},
['from']={'from','a'},
['until']={'until','a'},
['uncertain']={'uncertain','r'},
['possibly']={'possibly','r'},
['probably']={'probably','r'},
['presumably']={'presumably','r'},
-- eras
['bc']={'bc','e'},
['bce']={'bc','e'},
['before present']={'bp','e'},
['bp']={'bp','e'},
['ce']={'ad','e'},
['ad']={'ad','e'},
['ah']={'ah','e'},
-- conjunctions
['&']={'and','j'},
['and']={'and','j'},
['or']={'or','j'},
['-']={'from-until','j'},
['–']={'from-until','j'},
['from-until']={'from-until','j'},
['between']={'between','j'},
['circa2']={'circa2','a'},
-- precision
['day']={'day','p'},
['month']={'month','p'},
['year']={'year','p'},
['s']={'decade','p'},
['decade']={'decade','p'},
['century']={'century','p'},
['millennium']={'millennium','p'},
['million']={'million','p'},
['billion']={'billion','p'},
-- calendar
['julian']={'julian','c'},
['islamic']={'islamic','c'},
['hijri']={'islamic','c'},
-- "Turn of the ..." options are a combos which will be handled in similar way as calendars
['turn of the century']={'turn of the century','c'},
['century turn']={'turn of the century','c'},
['turn of the decade']={'turn of the decade','c'},
['decade turn']={'turn of the decade','c'},
['turn of the year']={'turn of the year','c'},
['year turn']={'turn of the year','c'},
-- other dates which will be handled in similar way as calendars
['?']={'unknown','c'},
['unknown']={'unknown','c'},
['unknown year']={'year unknown','c'},
['year unknown']={'year unknown','c'},
}
return p
fd1aqpq1a70t92o3nkj4hzkl8l48w6f
803609
803608
2019-02-26T00:19:07Z
en>Pppery
0
Commons version of [[Module:Roman]]
803609
Scribunto
text/plain
--[[
__ __ _ _ ___ _ ___ __ _ _ _
| \/ | ___ __| |_ _| | ___ _|_ _/ |( _ ) _ __ / /__ ___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_)| || |/ _ \| '_ \ / / __/ _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_ | || | (_) | | | |/ / (_| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)___|_|\___/|_| |_/_/ \___\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This code was copied from Commons please request changes there.
Maintainers:
*Jarekt
]]
local function Ordinal(...)
return require('Module:Ordinal-cd')._Ordinal(...)
end
local function Roman(...)
return require('Module:Roman-cd')._Numeral(...)
end
-- ==========================================================================
-- === Local Helper functions ==============================================
-- ==========================================================================
-- form of French "of" based on "precision", which is wikibase compatible date precision format:
-- 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- 1 2 3 4 5 mill cent deca year month day
local frOf = {'du','du','du','du','du', 'du', 'du', 'de', 'de', 'de', 'du'}
local function decYear(state,lang,case)
--Declension of word "year" in several languages
if state.precision == 9 then
yearCase = {
fi={xxx='vuosien', gen='vuoden', ill='vuoteen', ptv='vuotta', ela='vuodesta', ine='vuonna'},
sl={insP='letoma', ins='letom', gen='leta', loc='letu'},
hsb={insP='lětami', ins='lětom', gen='lěta', loc='lěće'}
}
return yearCase[lang][case] .. ' '
else
return ''
end
end
local function decCentury(lang,case)
--Declension of word "century" in several languages
centuryCase = {
pl={nom='wiek', gen='wieku', ins='wiekiem', loc='wieku', default = 'wiek'},
hsb={nom='lětstotk', gen='lětstotka', ins='lětstotkom', loc='lětstotku', default = 'lětstotk'}
}
return centuryCase[lang][case] or centuryCase[lang]['default']
end
local function decMillennium(lang,case)
--Declension of word "century" in several languages
millenniumCase = {
pl={nom='tysiąclecie', gen='tysiąclecia', ins='tysiącleciem', loc='tysiącleciu', default = 'tysiąclecie'},
hsb={nom='lěttysac', gen='lěttysaca', ins='lěttysacom', loc='lěttysacu', default = 'lěttysac'}
}
return millenniumCase[lang][case] or millenniumCase[lang]['default']
end
-- ==========================================================================
-- === Output data structure ===============================================
-- ==========================================================================
local p = {}
--[[
Multiple formats are possible:
1) string with key words "$date1" and "$date2" which will be replaced later by the date strings
2) function which can be used to create output string
* Function INPUTS:
- date1 and date2 string with date strings. "$date1" and "$date2" can also be used which will be replaced latter
- state is a table with full description of the phrase we are trying to construct. It includes state.precision1 (and 2)
which is wikibase compatible date precision format: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
3) table with following fields:
* table[1] is either string or a function
* table.case is a 2 element table with Grammatical cases used by given language to alter
month name or other adjective related to date # 1 and 2
]]
p.Translations = {
-- ==========================================
-- === Two date phrases =====================
-- ==========================================
['from-until'] = {
nDates = 2,
ar='من $date1 إلى $date2',
['be-tarask']={'з $date1 да $date2', case={'gen','gen'}},
bn='$date1 থেকে $date2 পর্যন্ত',
ca='$date1 fins $date2',
cs='$date1 až $date2',
da='fra $date1 til $date2',
de='$date1 bis $date2',
el='από $date1 έως $date2',
en='from $date1 until $date2',
['en-gb']='from $date1 until $date2',
['en-ca']='from $date1 until $date2',
eo='de $date1 al $date2',
es='de $date1 a $date2',
et='$date1 kuni $date2',
fa='از $date1 تا $date2',
fi='$date1–$date2', -- välisenä aikana
fr=function(date1, date2, state) -- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision > 10 then -- precision > month
return 'du $date1 au $date2'
else -- "DE septebmbre" / "D'octobre"
return require("Module:Linguistic").of(date1, 'fr') .. " à $date2"
end
end,
gl='de $date1 a $date2', --Template:From until used 'desde $date1 ata $date2'
he='מ- $date1 עד $date2',
hr='od $date1 do $date2',
hsb={'wot $date1 do $date2', case={'gen','gen'}},
hu='$date1 – $date2',
it='dal $date1 al $date2',
ja='$date1から$date2まで',
mk='од $date1 до $date2',
ml='$date1 മുതൽ $date2 വരെ',
nb='fra $date1 til $date2',
nds='$date1 bet $date2',
nn='fra $date1 til $date2',
nl='$date1 tot $date2',
no='fra $date1 til $date2',
pl={'od $date1 do $date2', case={'gen','gen'}},
ro='din $date1 până în $date2',
pt='de $date1 a $date2',
qu="$date1 p'unchawmanta $date2 p\'unchawkama",
ru={'с $date1 по $date2', case={'gen','gen'}},
sk={'od $date1 do $date2', case={'gen','gen'}},
sl={'od $date1 do $date2', case={'gen','gen'}},
sv='från $date1 till $date2',
uk={'з $date1 до $date2', case={'gen','gen'}},
ur='از $date1 تا $date2',
vi='từ $date1 đến $date2',
['zh-hans']='从$date1到$date2',
['zh-hant']='從$date1到$date2',
default='<span style="color:red">$date1 - $date2</span>',
},
['between']={
nDates = 2,
ar='بين $date1 و $date2',
['be-tarask']={'між $date1 і $date2', case={'ins','ins'}},
bn='$date1 এবং $date2-এর মাঝামাঝি',
ca='entre $date1 i $date2',
cs={'mezi $date1 a $date2', case={'ins','ins'}},
da='mellem $date1 og $date2',
de='zwischen $date1 und $date2',
el='μεταξύ $date1 και $date2',
en='between $date1 and $date2',
['en-gb']='between $date1 and $date2',
['en-ca']='between $date1 and $date2',
eo='inter $date1 kaj $date2',
es='entre $date1 y $date2',
et='$date1 ja $date2 vahel',
fa='بین $date1 و $date2',
fi={function(date1, date2, state) return decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='entre $date1 et $date2',
gl='entre $date1 e $date2',
he='בין $date1 ו- $date2',
hr='između $date1 i $date2',
hsb={'mjez $date1 a $date2', case={'ins','ins'}},
hu='$date1 és $date2 között',
hi='$date1 और $date2 के बीच',
it='tra il $date1 e il $date2',
ja='$date1と$date2の間',
mk='помеѓу $date1 и $date2',
ml='$date1, $date2 എന്നിവയ്ക്കിടയ്ക്ക്',
nb='mellom $date1 og $date2',
nds='twischen $date1 un $date2',
nn='mellom $date1 og $date2',
nl='tussen $date1 en $date2',
no='mellom $date1 og $date2',
pl={'między $date1 a $date2', case={'ins','ins'}},
ro='între $date1 și $date2',
pt='entre $date1 e $date2',
qu="$date1 p'unchawpa $date2 p'unchawpapas chawpin",
ru={'между $date1 и $date2', case={'ins','ins'}},
sk={'medzi $date1 a $date2', case={'ins','ins'}},
sl={function(date1, date2, state) return 'med ' .. decYear(state,'sl','insP') .. '$date1 in $date2' end, case={'ins','ins'}},
sv='mellan $date1 och $date2',
uk={'між $date1 та $date2', case={'ins','ins'}},
ur='بین $date1 و $date2',
vec='tra el $date1 e el $date2',
vi='giữa $date1 và $date2',
['zh-hans']='在$date1到$date2之间',
['zh-hant']='在$date1到$date2之間',
default='<span style="color:red">$date1 - $date2</span>',
},
['or'] = {
nDates = 2,
ar='$date1 أو $date2',
['be-tarask']='$date1 ці $date2',
bn='$date1 অথবা $date2',
ca='$date1 o $date2',
cs='$date1 nebo $date2',
da='$date1 eller $date2',
de='$date1 oder $date2',
el='$date1 ή $date2',
en='$date1 or $date2',
['en-gb']='$date1 or $date2',
['en-ca']='$date1 or $date2',
eo='$date1 aŭ $date2',
es='$date1 o $date2',
et='$date1 või $date2',
fa='$date1 یا $date2',
fi='$date1 tai $date2',
fr='$date1 ou $date2',
gl='$date1 ou $date2',
he='$date1 או $date2',
hr='$date1 ili $date2',
hsb='$date1 abo $date2',
hu='$date1 vagy $date2',
it='$date1 o $date2',
ja='$date1または$date2',
mk='$date1 или $date2',
ml='$date1 അഥവാ $date2',
nb='$date1 eller $date2',
nn='$date1 eller $date2',
nds='$date1 oder $date2',
nl='$date1 of $date2',
no='$date1 eller $date2',
pl='$date1 lub $date2',
pt='$date1 ou $date2',
qu='$date1 icha $date2',
ro='$date1 sau $date2',
ru='$date1 или $date2',
sk='$date1 alebo $date2',
sl='$date1 ali $date2',
sv='$date1 eller $date2',
uk='$date1 або $date2',
ur='$date1 یا $date2',
th='$date1 หรือ $date2',
vi='$date1 hoặc $date2',
['zh-hans']='$date1或$date2',
['zh-hant']='$date1或$date2',
default=function(date1, date2, state) return require("Module:linguistic").conj({date1, date2}, state.lang, 'or') end,
},
['and'] = {
nDates = 2,
ar='$date1 و $date2',
['be-tarask']='$date1 і $date2',
bn='$date1 এবং $date2',
ca='$date1 i $date2',
cs='$date1 a $date2',
da='$date1 og $date2',
de='$date1 und $date2',
el='$date1 και $date2',
en='$date1 and $date2',
['en-gb']='$date1 and $date2',
['en-ca']='$date1 and $date2',
eo='$date1 kaj $date2',
es='$date1 y $date2',
et='$date1 ja $date2',
fa='$date1 و $date2',
fi='$date1 ja $date2',
fr='$date1 et $date2',
gl='$date1 e $date2',
he='$date1 ו- $date2',
hu='$date1 és $date2',
hsb='$date1 a $date2',
it='$date1 e $date2',
ja='$date1および$date2',
mk='$date1 и $date2',
ml='$date1 ഒപ്പം $date2',
nb='$date1 og $date2',
nn='$date1 og $date2',
nds='$date1 un $date2',
nl='$date1 en $date2',
no='$date1 og $date2',
pl='$date1 i $date2',
pt='$date1 e $date2',
qu='$date1wan $date2pas',
ro='$date1 și $date2',
ru='$date1 и $date2',
sk='$date1 a $date2',
sl='$date1 in $date2',
sv='$date1 och $date2',
uk='$date1 та $date2',
ur='$date1 و $date2',
vi='$date1 và $date2',
['zh-hans']='$date1和$date2',
['zh-hant']='$date1和$date2',
default=function(date1, date2, state) return require("Module:linguistic").conj({date1, date2}, state.lang, 'and') end,
},
-- ==========================================
-- === Prepositions =========================
-- ==========================================
['by'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'да $date1', case={'gen',''}},
bn='$date1 কর্তৃক',
ca='vers $date1',
cs={'kolem $date1', case={'gen',''}},
da='mod $date1',
de='spätestens $date1',
el='έως $date1',
en='by $date1',
['en-gb']='by $date1',
['en-ca']='by $date1',
eo='ne post $date1',
es='para $date1',
et='hiljemalt $date1',
fa='توسط $date1',
fi={function(date1, state) return decYear(state,'fi','ill') .. '$date1 mennessä' end, case={'ill',''}},
fr='$date1 (au plus tard)',
gl='cara a $date1',
he='עד $date1',
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='entro il $date1',
ja='$date1まで',
mk='кон $date1',
ml='$date1-ൽ',
nb='innen $date1',
nn='innan $date1',
nds='$date1 oder vördem al',
nl='tegen $date1',
no='innen $date1',
pl={'przed $date1', case={'ins',''}},
pt='por $date1',
qu="$date1 p'unchawkama",
ro='de $date1',
ru={'к $date1', case={'dat',''}},
sk={'okolo $date1', case={'gen',''}},
sl={function(date1, state) return 'najpozneje ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='senast $date1',
uk={'до $date1', case={'dat',''}},
ur='توسط $date1',
vi='vào $date1', -- need to insert "đã" somewhere later in the sentence -->
['zh-hans']='$date1前后',
['zh-hant']='$date1前后',
default='<span style="color:red">by $date1</span>',
},
['before'] = {
nDates = 1,
ar='قبل $date1',
['be-tarask']={'перад $date1', case={'ins',''}},
bn='$date1-এর আগে',
bg='Преди $date1',
ca='abans $date1',
cs={'před $date1', case={'ins',''}},
da='før $date1',
de='vor $date1',
el='πριν από $date1',
en='before $date1',
['en-gb']='before $date1',
['en-ca']='before $date1',
eo='antaŭ $date1',
es='antes de $date1',
et='enne $date1',
fa='پیش از $date1',
fi={function(date1, state) return 'ennen ' .. decYear(state,'fi','ptv') .. '$date1' end, case={'ptv',''}},
fr='avant $date1',
gl='antes do $date1',
he='לפני $date1',
hr='prije $date1',
hsb={'do $date1', case={'gen',''}},
hu='$date1 előtt',
it='prima del $date1',
ja='$date1より前',
mk='пред $date1',
ml='$date1-നു മുമ്പ്',
nb='før $date1',
nn='før $date1',
nds='vör $date1',
nl='voor $date1',
no='før $date1',
pl={'przed $date1', case={'ins',''}},
pt='antes de $date1',
qu="$date1 p'unchaw ñawpan",
ro='înainte de $date1',
ru={'ранее $date1', case={'gen',''}},
sk={'pred $date1', case={'ins',''}},
sl={function(date1, state) return 'pred ' .. decYear(state,'sl','ins') .. ' $date1' end, case={'ins',''}},
sv='före $date1',
uk={'до $date1', case={'gen',''}},
ur='قبل $date1',
vi='trước $date1',
['zh-hans']='在$date1之前',
['zh-hant']='在$date1之前',
default='<span style="color:red">before $date1</span>',
},
['from'] = {
nDates = 1,
ar='من $date1',
['be-tarask']={'з $date1', case={'gen',''}},
bn='$date1 থেকে',
cs={'od $date1', case={'gen',''}},
de='Seit $date1',
el='από $date1',
en='from $date1',
['en-gb']='from $date1',
['en-ca']='from $date1',
eo='ekde $date1',
es='desde $date1',
et='alates $date1',
fa='از $date1',
fi={function(date1, state) return decYear(state,'fi','ela') .. '$date1 alkaen' end, case={'ela',''}},
fr= function(date1, state) return 'à partir '.. frOf[state.precision] ..' $date1' end,
gl='desde o $date1',
he='מ- $date1',
hr='od $date1',
hsb={'wot $date1', case={'gen',''}},
it='dal $date1',
ja='$date1から',
nds='vonaf $date1',
mk='од $date1',
nb='fra $date1',
nn='frå $date1',
ml='$date1 മുതൽ',
nl='vanaf $date1',
no='fra $date1',
pl={'od $date1', case={'gen',''}},
qu="$date1 p'unchawmanta",
ro='din $date1',
ru={'С $date1', case={'gen',''}},
sk={'od $date1', case={'gen',''}},
sl={function(date1, state) return 'od ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='från $date1',
uk={'з $date1', case={'gen',''}},
ur='از $date1',
vi='từ $date1',
['zh-hans']='自$date1',
['zh-hant']='自$date1',
default='<span style="color:red">from $date1</span>',
},
['until'] = {
nDates = 1,
['be-tarask']={'усутыч да $date1', case={'gen',''}},
bn='$date1 পর্যন্ত',
cs={'do $date1', case={'gen', ''}},
de='bis $date1',
en='until $date1',
['en-gb']='until $date1',
['en-ca']='until $date1',
eo='ĝis $date1',
es='hasta $date1',
fr=function(date1, state)
if state.precision > 10 then -- precision > month
return "jusqu'au $date1"
else
return "jusqu'à $date1"
end
end,
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='al $date1',
nb='til $date1',
nl='tot $date1',
nn='til $date1',
ml='$date1 വരെ',
pl={'do $date1', case={'gen',''}},
ru={'вплоть до $date1', case={'gen',''}},
sk={'do $date1', case={'gen', ''}},
uk={'аж до $date1', case={'gen',''}},
ur='تک $date1',
['zh-hans']='直到$date1',
['zh-hant']='直到$date1',
default='<span style="color:red">until $date1</span>',
},
['after'] = {
nDates = 1,
ar='بعد $date1',
['be-tarask']={'пасьля $date1', case={'gen',''}},
bn='$date1-এর পরে',
ca='després de $date1',
cs={'po $date1', case={'loc',''}},
de='nach $date1',
da='efter $date1',
el='μετά από $date1',
en='after $date1',
['en-gb']='after $date1',
['en-ca']='after $date1',
eo='post $date1',
es='después de $date1',
et='pärast $date1',
fa='پس از $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 jälkeen' end, case={'gen',''}},
fr='après $date1',
gl='despois do $date1',
he='לאחר $date1',
hr='nakon $date1',
hu='$date1 után',
hsb={'po $date1', case={'loc',''}},
it='dopo il $date1',
ja='$date1より後',
mk='по $date1',
ml='$date1-നു ശേഷം',
nb='etter $date1',
nn='etter $date1',
nds='na $date1',
nl='na $date1',
no='etter $date1',
pl={'po $date1', case={'loc',''}},
pt='após $date1',
qu="$date1 p'unchaw qhipan",
ro='după $date1',
ru={'после $date1', case={'gen',''}},
sk={'po $date1', case={'loc',''}},
sl={function(date1, state) return 'po ' .. decYear(state,'sl','loc') .. ' $date1' end, case={'loc',''}},
sv='efter $date1',
uk={'після $date1', case={'gen',''}},
ur='بعد $date1',
vi='sau $date1',
['zh-hans']='在$date1之后',
['zh-hant']='在$date1之後',
default='<span style="color:red">after $date1</span>',
},
-- ==========================================
-- === Adjectives ===========================
-- ==========================================
['early'] = {
nDates = 1,
ar='بداية $date1',
['be-tarask']={'пачатак $date1', case={'gen',''}},
bn='$date1-এর শুরুতে',
ca='al principi de $date1',
cs={'začátek $date1', case={'gen',''}},
da='tidlig $date1',
de='Anfang $date1',
el='αρχές του $date1',
en='early $date1',
['en-gb']='early $date1',
['en-ca']='early $date1',
eo='komence de $date1',
es='a principios de $date1',
et='$date1 algus',
fa='اوایل $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alku' end, case={'gen',''}},
fr='début $date1',
gl='a principios de $date1',
he='תחילת $date1',
hr='početak $date1',
hsb={'spočatk $date1', case={'gen',''}},
hu='$date1 elején',
it='inizio del $date1',
ja='$date1初頭',
mk='почеток на $date1',
ml='$date1 ആദ്യപാദം',
nb='tidlig $date1',
nn='tidleg $date1',
nds='Anfang $date1',
nl='begin $date1',
no='tidlig $date1',
pl={'początek $date1', case={'gen',''}},
pt='a princípios de $date1',
qu="$date1 ñawpaq p'unchawkunapi",
ro='începând cu $date1',
ru={'начало $date1', case={'gen',''}},
sk={'začiatok $date1', case={'gen',''}},
sl=function(date1, state)-- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision == 9 then -- precision > month
return 'zgodnje leto $date1'
else -- "DE septebmbre" / "D'octobre"
return 'zgodnji $date1'
end
end,
sv='början av $date1',
uk={'початок $date1', case={'gen',''}},
ur='ابتدا $date1',
vi='đầu năm $date1',
['zh-hans']='$date1早期',
['zh-hant']='$date1早期',
default='<span style="color:red">early $date1</span>',
},
['mid'] = {
nDates = 1,
ar='منتصف $date1',
['be-tarask']={'сярэдзіна $date1', case={'gen',''}},
bn='$date1-এর মাঝামাঝি',
ca='a mitjan $date1',
cs={'polovina $date1', case={'gen',''}},
da='midten af $date1',
de='Mitte $date1',
el='μέσα του $date1',
en='mid $date1',
['en-gb']='mid $date1',
['en-ca']='mid $date1',
eo='meze de $date1',
es='a mediados de $date1',
et='$date1 keskel',
fa='اواسط $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 puoliväli' end, case={'gen',''}},
fr='mi $date1',
gl='a mediados de $date1',
he='אמצע $date1',
hr='sredina $date1',
hsb={'srjedź $date1', case={'gen',''}},
hu='$date1 közepén',
it='metà del $date1',
ja='$date1半ば',
mk='средина на $date1',
ml='$date1 മദ്ധ്യം',
nb='midten av $date1',
nn='midten av $date1',
nds='Midd $date1',
nl='midden $date1',
no='midten av $date1',
pl={'środek $date1', case={'gen',''}},
pt='meados de $date1',
qu="$date1 chawpin p'unchawkunapi",
ro='mijlocul lui $date1',
ru={'середина $date1', case={'gen',''}},
sk={'polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'sredina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='mitten av $date1',
uk={'середина $date1', case={'gen',''}},
ur='وسط $date1',
vi='giữa năm $date1',
['zh-hans']='$date1中期',
['zh-hant']='$date1中期',
default='<span style="color:red">mid $date1</span>',
},
['late'] = {
nDates = 1,
ar='نهاية $date1',
bn='$date1-এর শেষে',
['be-tarask']={'канец $date1', case={'gen',''}},
ca='al final de $date1',
cs='konec $date1',
da='sen $date1',
de='Ende $date1',
el='τέλη του $date1',
en='late $date1',
['en-gb']='late $date1',
['en-ca']='late $date1',
eo='fine de $date1',
es='a fines de $date1',
et='$date1 lõpp',
fa='اواخر $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppu' end, case={'gen',''}},
fr='fin $date1',
gl='a finais de $date1',
he='סוף $date1',
hr='kraj $date1',
hsb={'kónc $date1', case={'gen',''}},
hu='$date1 végén',
it='fine del $date1',
ja='$date1末',
mk='крајот на $date1',
ml='$date1 അന്ത്യപാദം',
nb='sent $date1',
no='seint $date1',
nds='Enn $date1',
nl='eind $date1',
no='sent $date1',
pl={'koniec $date1', case={'gen',''}},
pt='ao final de $date1',
qu="$date1 qhipaq p'unchawkunapi",
ro='sfârșitul lui $date1',
ru={'конец $date1', case={'gen',''}},
sk={'koniec $date1', case={'gen',''}},
sl={function(date1, state) return 'pozno ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='slutet av $date1',
uk={'кінець $date1', case={'gen',''}},
ur='اخیر $date1',
vi='cuối năm $date1',
['zh-hans']='$date1晚期',
['zh-hant']='$date1晚期',
default='<span style="color:red">late $date1</span>',
},
['circa'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'каля $date1', case={'gen',''}},
bn='প্রায় $date1',
ca='cap a $date1',
cs='cca $date1',
da='ca. $date1',
de='etwa $date1',
el='περ. $date1',
en='circa $date1',
['en-gb']='circa $date1',
['en-ca']='circa $date1',
eo='ĉirkaŭ $date1',
es='hacia $date1', -- suggestion use "hacia"/"hacia el" or leave the latin circa or ca.
et='ca. $date1',
fa='حوالی $date1', -- template:Circa used 'حدود $date1'
fi={function(date1, state) return 'suunnilleen ' .. decYear(state,'fi','ine') .. '$date1' end, case={'ine',''}}, -- template:Circa used 'noin $date1'
fr=function(date1, state)
if state.precision >= 11 then -- precision >= day
return 'vers le $date1'
else
return 'vers $date1'
end
end,
gl='entre $date1', -- template:Circa used 'cara a $date1'
he='בסביבות $date1',
hr='oko $date1',
hsb={'wokoło $date1', case={'gen',''}},
hu='$date1 körül',
it='$date1 circa',
ja='$date1頃',
ko='$date1경',
mk='~ $date1', -- template:Circa used 'o. $date1'
ml= 'ഉദ്ദേശം $date1',
nb='ca. $date1',
nn='ca. $date1',
nds='üm un bi $date1',
nl='ca. $date1',
no='ca. $date1',
pl={'około $date1', case={'gen',''}},
pt='cerca de $date1',
qu='$date1chá',
ro='circa $date1',
ru={'около $date1', case={'gen',''}},
sk='cca $date1',
sl={function(date1, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='c:a $date1', -- template:Circa used 'ca $date1'
uk={'близько $date1', case={'gen', ''}},
ur='قریباً $date1',
vi='vào khoảng $date1',
['zh-hans']='约$date1',
['zh-hant']='約$date1',
default='<span style="color:red">circa $date1</span>',
},
['uncertain'] = {
nDates = 1,
['zh-hans']='$date1(?)',
['zh-hant']='$date1(?)',
default='$date1 (?)',
},
probably = { -- see Q56644435
nDates = 1,
ast = 'probablemente $date1',
bn = 'সম্ভবত $date1',
ca = 'probablement $date1',
cs = 'pravděpodobně $date1',
de = 'wahrscheinlich $date1',
el = 'πιθανόν $date1',
en = 'probably $date1',
['en-gb']='probably $date1',
['en-ca']='probably $date1',
eo = 'probable $date1',
es = 'probable $date1',
fa = 'احتمالاً $date1',
fr = 'probablement $date1',
he = 'כפי הנראה $date1',
hu = 'feltehetőleg $date1',
it = 'probabile $date1',
mk = 'веројатно $date1',
ml = 'ഒരുപക്ഷേ $date1',
nl = 'waarschijnlijk $date1',
pl = 'prawdopodobnie $date1',
pt = 'provavelmente $date1',
ru = 'вероятно $date1',
sv = 'antagligen $date1',
uk = 'ймовірно $date1',
default='<span style="color:red">probably $date1</span>',
},
possibly = { -- see Q30230067
nDates = 1,
ast = 'posiblemente $date1',
bn = 'যথাসম্ভব $date1',
cs = 'možná $date1',
de = 'möglicherweise $date1',
en = 'possibly $date1',
['en-gb']='possibly $date1',
['en-ca']='possibly $date1',
eo = 'eble $date1',
el = 'πιθανόν $date1',
fa = 'احتمالاً $date1',
fr = 'peut-être $date1',
hu = 'talán $date1',
mk = 'можеби $date1',
ml = 'മിക്കവാറും $date1',
nl = 'mogelijk $date1',
pl = 'być może $date1',
pt = 'possivelmente $date1',
ro = 'poate $date1',
ru = 'возможно $date1',
sv = 'möjligen $date1',
uk = 'можливо $date1',
default='<span style="color:red">possibly $date1</span>',
},
presumably = { -- see Q18122778
nDates = 1,
ast = 'presumiblemente $date1',
de = 'vermutlich $date1',
en = 'presumably $date1',
['en-gb']='presumably $date1',
['en-ca']='presumably $date1',
fr = 'présumé $date1',
it = 'presumibilmente $date1',
ml = 'ഊഹപ്രകാരം $date1',
ru = 'предположительно $date1',
pl = 'przypuszczalnie $date1',
uk = 'здогадно $date1',
default='<span style="color:red">presumably $date1</span>',
},
['year unknown']= {
nDates = 0,
ca='Any desconegut',
['be-tarask']='Год невядомы',
bn='অজানা বছর',
cs='neznámý rok',
de='Jahr unbekannt',
el='άγνωστο έτος',
en='year unknown',
['en-gb']='year unknown',
['en-ca']='year unknown',
eo='jaro nesciata',
es='Año desconocido',
fa='سال نامعلوم',
fi='tuntematon vuosi',
fr='Année inconnue',
he='שנה לא ידועה',
hsb='lěto njeznate',
hr='nepoznata godina',
it='anno sconosciuto',
ja='年代不明',
la='Sine anno',
ml='വർഷം ലഭ്യമല്ല',
mk='непозната година',
nb='ukjent år',
nn='ukjent år',
nl='zonder jaar',
no='ukjent år',
pl='rok nieznany',
qu='Mana riqsisqa wata',
ru='Год неизвестен',
sk='neznámy rok',
sl='neznano leto',
sv='Okänt år',
uk='рік невідомий',
ur='سال نامعلوم',
vi='không rõ năm',
['zh-hans']='年份未知',
['zh-hant']='年份未知',
default='<span style="color:red">year unknown</span>',
},
-- ==========================================
-- === Parts of a whole =====================
-- ==========================================
['firsthalf'] = {
nDates = 1,
ar='النصف الأول من $date1',
['be-tarask']={'першая палова $date1', case={'gen',''}},
bn='$date1-এর প্রথমার্ধ',
ca='1a meitat de $date1',
cs={'1. polovina $date1', case={'gen',''}},
da='1. halvdel af $date1',
de='1. Hälfte $date1',
el='πρώτο μισό του $date1',
en='first half of $date1',
['en-gb']='first half of $date1',
['en-ca']='first half of $date1',
eo='unua duono de $date1',
es='primera mitad de $date1',
et='$date1 esimene pool',
fa='نیمهٔ نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alkupuolisko' end, case={'gen',''}},
fr='1<sup>re</sup> moitié de $date1',
gl='primeira metade de $date1',
he='במחצית הראשונה של $date1',
hsb={'prěnja połojca $date1', case={'gen',''}},
hu='$date1 első felében',
hr='prva polovina $date1',
it='prima metà del $date1',
ja='$date1上半期',
mk='прва половина на $date1',
ml='$date1 ആദ്യപകുതി',
nb='første halvdel av $date1',
nn='fyrste halvdelen av $date1',
nds='1. Helft $date1',
nl='eerste helft $date1',
no='første halvdel av $date1',
pl={'pierwsza połowa $date1', case={'gen',''}},
pt='primeira metade de $date1',
qu='$date1 ñawpaq kuskanpi',
ro='prima jumătate a lui $date1',
ru={'первая половина $date1', case={'gen',''}},
sk={'1. polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'prva polovica ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första halvan av $date1',
uk={'перша половина $date1', case={'gen',''}},
ur='نصف اول $date1',
vi='nửa đầu năm $date1',
['zh-hans']='$date1上半期',
['zh-hant']='$date1上半期',
default='<span style="color:red">first half of $date1</span>',
},
['secondhalf'] = {
nDates = 1,
ar='النصف الثاني من $date1',
['be-tarask']={'другая палова $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয়ার্ধ',
ca='2a meitat de $date1',
cs={'2. polovina $date1', case={'gen',''}},
da='2. halvdel af $date1',
de='2. Hälfte $date1',
el='δεύτερο μισό του $date1',
en='second half of $date1',
['en-gb']='second half of $date1',
['en-ca']='second half of $date1',
eo='dua duono de $date1',
es='segunda mitad de $date1',
et='$date1 teine pool',
fa='نیمهٔ دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppupuolisko' end, case={'gen',''}},
fr='2<sup>e</sup> moitié de $date1',
gl='segunda metade de $date1',
he='במחצית השנייה של $date1',
hsb={'druha połojca $date1', case={'gen',''}},
hr='druga polovina $date1',
hu='$date1 második felében',
it='seconda metà del $date1',
ja='$date1下半期',
mk='втора половина на $date1',
ml='$date1 രണ്ടാം പകുതി',
nb='andre halvdel av $date1',
nn='andre halvdelen av $date1',
nds='2. Helft vun $date1',
nl='tweede helft $date1',
no='andre halvdel av $date1',
pl={'druga połowa $date1', case={'gen',''}},
pt='segunda metade de $date1',
qu='$date1 qhipaq kuskanpi',
ro='a doua jumătate a lui $date1',
ru={'вторая половина $date1', case={'gen',''}},
sk={'2. polovica $date1', case={'gen',''}},
sl={'druga polovica {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sv='andra halvan av $date1',
uk={'друга половина $date1', case={'gen',''}},
ur='نصف آخر $date1',
vi='nửa cuối năm $date1',
['zh-hans']='$date1下半期',
['zh-hant']='$date1下半期',
default='<span style="color:red">second half of $date1</span>',
},
['1quarter']= {
nDates = 1,
bn='$date1-এর প্রথম ত্রৈমাসিক',
['be-tarask']={'першая чвэрць $date1', case={'gen',''}},
cs={'1. čtvrtina $date1', case={'gen',''}},
de='erstes Viertel $date1',
en='first quarter of $date1',
['en-gb']='first quarter of $date1',
['en-ca']='first quarter of $date1',
eo='unua kvarono de $date1',
es='primer cuarto del $date1',
fa='ربع نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 ensimmäinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'premier quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון ראשון של $date1',
hsb={'prěnja štwórćina $date1', case={'gen',''}},
hr='prva četvrtina $date1',
it='primo quarto del $date1',
ja='$date1第1四半期',
ml='$date1 ഒന്നാം ത്രൈമാസം',
nb='første kvartal av $date1',
nl='eerste kwart $date1',
nn='fyrste kvartalet av $date1',
no='første kvartal av $date1',
pl={'pierwsza ćwiartka $date1', case={'gen',''}},
qu='$date1 ñawpaq ñiqin tawa phatmanpi',
ru={'первая четверть $date1', case={'gen',''}},
sk={'1. štvrtina $date1', case={'gen',''}},
sl={'prva četrtina {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sl={function(date1, state) return 'prva četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första kvartalet av $date1',
uk={'1-ша чверть $date1', case={'gen',''}},
ur='پہلی سہ ماہی $date1',
vi='quý 1 năm $date1',
['zh-hans']='$date1第一季度',
['zh-hant']='$date1第一季度',
default='<span style="color:red">first quarter of $date1</span>',
},
['2quarter']= {
nDates = 1,
['be-tarask']={'другая чвэрць $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয় ত্রৈমাসিক',
cs={'2. čtvrtina $date1', case={'gen',''}},
de='zweites Viertel $date1',
en='second quarter of $date1',
['en-gb']='second quarter of $date1',
['en-ca']='second quarter of $date1',
eo='dua kvarono de $date1',
es='segundo cuarto del $date1',
fa='ربع دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 toinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'deuxième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שני של $date1',
hsb={'druha štwórćina $date1', case={'gen',''}},
hr='druga četvrtina $date1',
it='secondo quarto del $date1',
ja='$date1第2四半期',
ml='$date1 രണ്ടാം ത്രൈമാസം',
nb='andre kvartal av $date1',
nl='tweede kwart $date1',
nn='andre kvartalet av $date1',
no='andre kvartal av $date1',
pl={'druga ćwiartka $date1', case={'gen',''}},
qu='$date1 iskay ñiqin tawa phatmanpi',
ru={'вторая четверть $date1', case={'gen',''}},
sk={'2. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'druga četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='andra kvartalet av $date1',
uk={'2-га чверть $date1', case={'gen',''}},
ur='دوسری سہ ماہی $date1',
vi='quý 2 năm $date1',
['zh-hans']='$date1第二季度',
['zh-hant']='$date1第二季度',
default='<span style="color:red">second quarter of $date1</span>',
},
['3quarter']= {
nDates = 1,
['be-tarask']={'трэцяя чвэрць $date1', case={'gen',''}},
bn='$date1-এর তৃতীয় ত্রৈমাসিক',
cs={'3. čtvrtina $date1', case={'gen',''}},
de='drittes Viertel $date1',
en='third quarter of $date1',
['en-gb']='third quarter of $date1',
['en-ca']='third quarter of $date1',
eo='tria kvarono de $date1',
es='tercer cuarto del $date1',
fa='ربع سوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 kolmas neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'troisième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שלישי של $date1',
hsb={'třeća štwórćina $date1', case={'gen',''}},
hr='treća četvrtina $date1',
it='terzo quarto del $date1',
ja='$date1第3四半期',
ml='$date1 മൂന്നാം ത്രൈമാസം',
nb='tredje kvartal av $date1',
nl='derde kwart $date1',
nn='tredje kvartalet av $date1',
no='tredje kvartal av $date1',
pl={'trzecia ćwiartka $date1', case={'gen',''}},
qu='$date1 kimsa ñiqin tawa phatmanpi',
sk={'3. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'tretja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
ru={'третья четверть $date1', case={'gen',''}},
sv='tredje kvartalet av $date1',
uk={'3-тя чверть $date1', case={'gen',''}},
ur='تیسری سہ ماہی $date1',
vi='quý 3 năm $date1',
['zh-hans']='$date1第三季度',
['zh-hant']='$date1第三季度',
default='<span style="color:red">third quarter of $date1</span>',
},
['4quarter']= {
nDates = 1,
['be-tarask']={'чацьвёртая чвэрць $date1', case={'gen',''}},
bn='$date1-এর চতুর্থ ত্রৈমাসিক',
cs={'4. čtvrtina $date1', case={'gen',''}},
de='letztes Viertel $date1',
en='fourth quarter of $date1',
['en-gb']='fourth quarter of $date1',
['en-ca']='fourth quarter of $date1',
eo='kvara kvarono de $date1',
es='último cuarto del $date1',
fa='ربع چهارم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 viimeinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'quatrième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון רביעי של $date1',
hsb={'poslednja štwórćina $date1', case={'gen',''}},
hr='četvrta četvrtina $date1',
it='quarto quarto del $date1',
ja='$date1第4四半期',
ml='$date1 നാലാം ത്രൈമാസം',
nb='fjerde kvartal av $date1',
nl='vierde kwart $date1',
nn='tredje kvartalet av $date1',
no='fjerde kvartal av $date1',
pl={'czwarta ćwiartka $date1', case={'gen',''}},
qu='$date1 qhipaq ñiqin tawa phatmanpi',
ru={'последняя четверть $date1', case={'gen',''}},
sk={'4. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'zadnja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='fjärde kvartalet av $date1',
uk={'4-та чверть $date1', case={'gen',''}},
ur='چوتھی سہ ماہی $date1',
vi='quý 4 năm $date1',
['zh-hans']='$date1第四季度',
['zh-hant']='$date1第四季度',
default='<span style="color:red">forth quarter of $date1</span>',
},
-- ==========================================
-- === Seasons ==============================
-- ==========================================
['spring'] = {
nDates = 1,
ar='ربيع $date1',
['be-tarask']='вясна $date1',
bn='বসন্তকাল $date1',
ca='primavera $date1',
cs={'jaro $date1', case={'gen', ''}},
da='Forår $date1',
de='Frühling $date1',
en='Spring $date1',
['en-gb']='Spring $date1',
['en-ca']='Spring $date1',
eo='printempo $date1',
es='primavera de $date1',
et='$date1 kevad',
fa='بهار $date1',
fi='Kevät $date1',
fr='printemps $date1',
gl='primavera $date1',
he='אביב $date1',
hr='proljeće $date1',
hsb='nalěćo $date1',
hu='$date1 tavasza',
it='primavera $date1',
ja='{{#if:$date1|$date1年}}春',
mk='пролет $date1',
ml='$date1 വസന്തം',
nb='våren $date1',
nds='Vörjohr $date1',
nn='våren $date1',
nl='voorjaar $date1',
no='våren $date1',
pl='wiosna $date1',
pt='primavera de $date1',
ro='primăvara lui $date1',
ru='весна $date1',
sk={'jar $date1', case={'gen', ''}},
sl='pomlad $date1',
sv='våren $date1',
uk='весна $date1',
ur='بہار $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}春季',
['zh-hant']='{{#if:$date1|$date1年}}春季',
default='<span style="color:red">Spring $date1</span>',
},
['summer'] = {
nDates = 1,
ar='صيف $date1',
['be-tarask']='лета $date1',
bn='গ্রীষ্মকাল $date1',
ca='estiu $date1',
cs={'léto $date1', case={'gen', ''}},
da='Sommer $date1',
de='Sommer $date1',
en='Summer $date1',
['en-gb']='Summer $date1',
['en-ca']='Summer $date1',
eo='somero $date1',
es='verano de $date1',
et='$date1 suvi',
fa='تابستان $date1',
fi='Kesä $date1',
fr='été $date1',
gl='verán $date1',
he='קיץ $date1',
hr='ljeto $date1',
hsb='lěćo $date1',
hu='$date1 nyara',
it='estate $date1',
ja='{{#if:$date1|$date1年}}夏',
mk='лето $date1',
ml='$date1 വേനൽ',
nb='sommeren $date1',
nds='Sommer $date1',
nn='sumaren $date1',
nl='zomer $date1',
no='sommeren $date1',
pl='lato $date1',
pt='verão de $date1',
ro='vara lui $date1',
ru='лето $date1',
sk={'leto $date1', case={'gen', ''}},
sl='poletje $date1',
sv='sommaren $date1',
uk='літо $date1',
ur='گرما $date1',
vi='mùa hè năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}夏季',
['zh-hant']='{{#if:$date1|$date1年}}夏季',
default='<span style="color:red">Summer $date1</span>',
},
['autumn'] = {
nDates = 1,
ar='خريف $date1',
['be-tarask']='восень $date1',
bn='শরত্কাল $date1',
ca='tardor $date1',
cs={'podzim $date1', case={'gen', ''}},
da='Efterår $date1',
de='Herbst $date1',
en='Autumn $date1',
['en-gb']='Autumn $date1',
['en-ca']='Autumn $date1',
eo='aŭtuno $date1',
es='otoño de $date1',
et='$date1 sügis',
fa='پاییز $date1',
fi='Syksy $date1',
fr='automne $date1',
gl='outono $date1',
he='סתיו $date1',
hr='jesen $date1',
hsb='nazyma $date1',
hu='$date1 ősze',
it='autunno $date1',
ja='{{#if:$date1|$date1年}}秋',
mk='есен $date1',
ml='$date1 ശരത്കാലം',
nb='høsten $date1',
nds='Harvst $date1',
nn='hausten $date1',
nl='herfst $date1',
no='høsten $date1',
pl='jesień $date1',
pt='outono de $date1',
ro='toamna lui $date1',
ru='осень $date1',
sk={'jeseň $date1', case={'gen', ''}},
sl='jesen $date1',
sv='hösten $date1',
uk='осінь $date1',
ur='خزاں $date1',
vi='mùa thu năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}秋季',
['zh-hant']='{{#if:$date1|$date1年}}秋季',
default='<span style="color:red">Autumn $date1</span>',
},
['winter'] = {
nDates = 1,
ar='شتاء $date1',
['be-tarask']='зіма $date1',
bn='শীতকাল $date1',
ca='hivern $date1',
cs={'zima $date1', case={'gen', ''}},
da='Vinter $date1',
de='Winter $date1',
en='Winter $date1',
['en-gb']='Winter $date1',
['en-ca']='Winter $date1',
eo='vintro $date1',
es='invierno de $date1',
et='$date1 talv',
fa='زمستان $date1',
fi='Talvi $date1',
fr='hiver $date1',
gl='inverno $date1',
he='חורף $date1',
hr='zima $date1',
hsb='zyma $date1',
hu='$date1 tele',
it='inverno $date1',
ja='{{#if:$date1|$date1年}}冬',
mk='зима $date1',
ml='$date1 ശൈത്യകാലം',
nds='Winter $date1',
nl='winter $date1',
nb='vinteren $date1',
nn='vinteren $date1',
no='vinteren $date1',
pl='zima $date1',
pt='inverno de $date1',
ro='iarna lui $date1',
ru='зима $date1',
sk={'zima $date1', case={'gen', ''}},
sl='zima $date1',
sv='vintern $date1',
uk='зима $date1',
ur='سرما $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}冬季',
['zh-hant']='{{#if:$date1|$date1年}}冬季',
default='<span style="color:red">Winter $date1</span>',
},
-- ==========================================
-- === Decade/Century/Millennium ============
-- ==========================================
['decade'] = {
nDates = 1,
ar='أعوام $date1',
['be-tarask']='$date1-я гады',
bn='$date1-এর দশক',
ca='dècada del $date1',
cs=function(date1, state) return ((date1 % 100 == 0) and 'první dekáda' or ((date1 % 100) .. '. léta')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. století' end,
da="$date1'erne",
de='$date1er-Jahre',
el='δεκαετία του $date1',
en='$date1s',
['en-gb']='$date1s',
['en-ca']='$date1s',
eo='$date1-aj jaroj',
es='años $date1',
et='$date1ndad',
fa='دههٔ $date1',
fi='$date1-luvulla',
fr='années $date1',
gl='anos $date1',
he='שנות ה-$date1',
hr='$date1-ih',
hsb='$date1te lěta',
hu='$date1-s évek',
it="{{#ifexpr: $date1 mod 100 = 0 | primo decennio del {{Roman|{{#expr: ($date1 / 100) + 1}}}} sec. | anni '{{#expr: $date1 mod 100 }} del {{Roman|{{#expr: floor($date1 / 100) + 1}}}} sec.}}",
ja='$date1年代',
mk='$date1-ти',
ml='ദശകം $date1-ൽ',
nb='$date1-årene',
nn='$date1-åra',
nds='Johren $date1',
nl='jaren $date1',
no='$date1-årene',
pl=function(date1, state)
local str = 'lata '..(date1 % 100)..'. '
if (date1 % 100 == 0) then
str = 'pierwsza dekada '
elseif (date1 % 100 == 10) then
str = 'druga dekada '
end
return str .. Roman(math.floor(date1 / 100) + 1) .. ' wieku'
end,
pt='década de $date1',
qu='$date1 watakuna',
ro='anii $date1',
ru='$date1-е годы',
sk=function(date1, state) return ((date1 % 100 == 0) and 'prvé desaťročie' or ((date1 % 100) .. '. roky')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. storočia' end,
sl='$date1-ta leta',
sv='$date1-talet{{#switch:$date1|1300|1400|1500|1600|1700|1800|1900|2000|2100= (decennium)}}',
uk='$date1-ті роки',
ur='دہائی $date1',
vi='thập niên $date1',
['zh-hans']='$date1年代',
['zh-hant']='$date1年代',
default='<span style="color:red">$date1s</span>',
},
['century'] = {
nDates = 1,
ar='القرن $date1',
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' стагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' শতাব্দী' end,
ca='segle {{small-caps|{{lc:{{Roman year|$date1}}}}}}',
cs='$date1. století',
da='$date1. århundrede',
de='$date1. Jahrhundert',
el='$date1ος αιώνας',
en=function(date1, state) return Ordinal(date1, 'en') .. ' century' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' century' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' century' end,
eo='$date1-a jarcento',
es=function(date1, state) return 'siglo ' .. Roman(date1) end,
et='$date1. sajand',
fa='سدهٔ $date1',
fi='{{#expr: ($date1 - 1) * 100}}-luvulla',
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle' end,
gl=function(date1, state) return 'século ' .. Roman(date1) end,
he='המאה ה-$date1',
hr='$date1. stoljeće',
hsb='$date1. lětstotk',
hu='$date1. évszázad',
it=function(date1, state) return Roman(date1) .. ' sec.' end,
ja='$date1世紀',
la='saeculum $date1',
mk=function(date1, state) return Roman(date1) .. ' век' end,
ml='$date1-ആം നൂറ്റാണ്ട്',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' århundre' end,
nn=function(date1, state) return 'det ' .. Ordinal(date1, 'no') .. ' århundre' end,
nds='$date1. Johrhunnert',
nl='$date1e eeuw',
no=function(date1, state) return Ordinal(date1, 'no') .. ' århundre' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decCentury('pl',state.case) end,
pt=function(date1, state) return 'século ' .. Roman(date1) end,
qu='$date1 ñiqin pachakwata',
ro='secolul al $date1-lea',
ru=function(date1, state) return Roman(date1) .. ' век' end,
sk='$date1. storočie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. stoletje' end,
sv='{{#expr:$date1 - 1}}00-talet',
tt=function(date1, state) return Roman(date1) .. ' гасыр' end,
uk=function(date1, state) return Roman(date1) .. ' ст.' end,
ur='صدی $date1',
vi='thế kỷ $date1',
['zh-hans']='$date1世纪',
['zh-hant']='$date1世紀',
default='<span style="color:red">$date1 century</span>',
},
['millennium'] = {
nDates = 1,
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' тысячагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' সহস্রাব্দ' end,
cs='$date1. tisíciletí',
de='$date1. Jahrtausend',
en=function(date1, state) return Ordinal(date1, 'en') .. ' millennium' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' millennium' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' millennium' end,
eo='$date1-a jarmilo',
es=function(date1, state) return Roman(date1) .. ' milenio' end,
fa=function(date1, state) return 'هزارهٔ ' .. Ordinal(date1, 'fa') end,
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' millénaire' end,
hr='$date1. tisućljeće',
hsb='$date1. lěttysac',
it=function(date1, state) return Roman(date1) .. ' millennio' end,
ja='$date1千年紀',
la='millennium $date1',
ml='$date1-മത്തെ സഹസ്രാബ്ദം',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' årtusen' end,
nn=function(date1, state) return Ordinal(date1, 'no') .. ' tusenår' end,
nl='$date1e millennium',
no=function(date1, state) return Ordinal(date1, 'no') .. ' årtusen' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decMillennium('pl',state.case) end,
pt=function(date1, state) return Roman(date1) .. ' milénio' end,
qu='$date1 ñiqin waranqawata',
ru=function(date1, state) return Roman(date1) .. ' тысячелетие' end,
sk='$date1. tisícročie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. tisočletje' end,
sv=function(date1, state) return Ordinal(date1, 'sv') .. ' årtusendet' end,
uk=function(date1, state) return Roman(date1) .. ' тис.' end,
ur=function(date1, state) return 'ہزارہ ' .. Ordinal(date1, 'ur') end,
vi='thiên niên kỷ $date1',
['zh-hans']=function(date1, state) return Ordinal(date1, 'zh-hans') .. '千年' end,
['zh-hant']=function(date1, state) return Ordinal(date1, 'zh-hant') .. '千年' end,
--default='{{Please translate|$date1 millennium|Module:I18n/complex date}}',
default='<span style="color:red">$date1 millennium</span>',
},
['million'] = {
de = '$date1 Millionen Jahre',
en = '$date1 million years',
['en-gb'] = '$date1 million years',
['en-ca'] = '$date1 million years',
eo = '$date1 milionoj da jaroj',
es = '$date1 millones de años',
nl = '$date1e miljoen jaar',
fr = '$date1 millions d’années',
it = '$date1 milioni di anni',
ml = function(date1, state)
if date1 >= 10 then
return (date1 / 10) .. ' കോടി വർഷങ്ങൾ' -- 100 lakh = 1 crore
else
return (date1 * 10) .. ' ലക്ഷം വർഷങ്ങൾ'
end
end,
nb = '$date1 millioner år',
nl = '$date1 miljoen jaar',
nn = '$date1 millionar år',
pl = '$date1 milionów lat',
pt = '$date1 milhões de anos',
ru = '$date1 миллиона лет',
uk = '$date1 мільйони років',
ur = '$date1 ملین سال',
['zh-hans'] = '$date1百万年',
['zh-hant'] = '$date1百万年',
default='<span style="color:red">$date1 million years</span>',
nDates = 1,
},
['billion'] = {
de = '$date1 Milliarden Jahre',
en = '$date1 billion years',
['en-gb'] = '$date1 billion years',
['en-ca'] = '$date1 billion years',
es = '$date1 mil millones de años',
nl = '$date1e miljard jaar',
fr = '$date1 milliard d’années',
it = '$date1 miliardi di anni',
ml = function(date1, state) return (date1 * 10) .. ' കോടി വർഷങ്ങൾ' end,
nb = '$date1 milliarder år',
nl = '$date1 miljard jaar',
nn = '$date1 milliardar år',
pl = '$date1 miliardów lat',
pt = '$date1 mil milhões de anos',
ru = '$date1 миллиардов лет',
uk = '$date1 мільярдів років',
ur = '$date1 بلین سال',
['zh-hans'] = '$date1十亿年',
['zh-hant'] = '$date1十億年',
default='<span style="color:red$date1 billion years</span>',
nDates = 1,
},
-- ==========================================
-- === Complex phrases ======================
-- ==========================================
-- Phrases which for some languages can be build out of simpler phrases
-- Their "default" state, used when no translation in the requested language was found, returns a "state" like
-- table which is used to call the function again and built the phrase out of simple phrases
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the century'] = {
nDates = 2,
['be-tarask']=function(date1, date2, state) return 'канец ' .. Roman(date1) ..' — пачатак ' .. Roman(date2) ..' стагодзьдзя' end,
bn=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'bn') .. ' শতাব্দীর পালাবদলে' end,
cs='přelom $date1. a $date2. století',
de=function(date1, date2, state) return 'Jahrhundertwende $date1/' .. Ordinal(date2, 'de') end,
en=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en') .. ' century' end,
['en-gb']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-gb') .. ' century' end,
['en-ca']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-ca') .. ' century' end,
eo='ŝanĝo de la $date1-a al la $date2-a jarcentoj',
es=function(date1, date2, state) return 'hacia finales del siglo ' .. Roman(date1) .. ' y principios del siglo ' .. Roman(date2) .. '' end,
fi='{{#expr: $date1 * 100-100}}-luvun loppu ja {{#expr: $date2 * 100-100}}-luvun alku',
fr=function(date1, date2, state) return 'fin du ' .. Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle / début du ' .. Ordinal(date2, 'fr', 'roman', 'm') end,
hr='kraj $date1. i početak $date2. stoljeća',
hsb=function(date1, date2, state) return 'přechod ' .. Roman(date1) ..' do ' .. Roman(date2) ..' lětstotka' end,
it=function(date1, date2, state) return 'fine del ' .. Roman(date1) .. ' sec. e inizio del ' .. Roman(date2) .. ' sec.' end,
ja='$date1世紀から$date2世紀への変わり目',
ml='$date1 നൂറ്റാണ്ട് അവസാനവും $date2 നൂറ്റാണ്ട് ആദ്യവും',
nb=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nb') .. ' århundre/begynnelsen av det ' .. Ordinal(date2, 'nb') .. ' århundre' end,
nl=function(date1, date2, state) return 'eind $date1e/begin $date2e eeuw' end,
nn=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nn') .. ' hundreåret/byrjinga av det ' .. Ordinal(date2, 'nn') .. ' hundreåret' end,
pl=function(date1, date2, state) return 'przełom ' .. Roman(date1) ..' i ' .. Roman(date2) ..' wieku' end,
qu="$date1 ñiqin waranqawatamanta $date2 ñiqin waranqawataman t'ikrasqanpi",
ru=function(date1, date2, state) return 'конец ' .. Roman(date1) ..' — начало ' .. Roman(date2) ..' века' end,
sk='prelom $date1. a $date2. storočia',
sl=function(date1, date2, state) return 'pozno ' .. Ordinal(date1, 'sl') .. '. in zgodnje ' .. Ordinal(date2, 'sl') .. '. stoletje' end,
sv=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'sv') .. ' årtusendets sekelskifte' end,
uk=function(date1, date2, state) return 'кін. ' .. Roman(date1) ..' ст. — поч. ' .. Roman(date2) ..' ст.' end,
vi='bước sang thế kỷ $date2',
['zh-hans']=function(date1, date2, state) return '$date1世纪末' .. Ordinal(date2, 'zh-hans') .. '世纪初' end,
['zh-hant']=function(date1, date2, state) return '$date1世紀末' .. Ordinal(date2, 'zh-hant') .. '世紀初' end,
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='century', era1=state.era1, adj2 = 'early', unit2='century', era2=state.era2} end,
},
-- $date1 - original date - 10
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the decade']={
nDates = 2,
['be-tarask']='канец $date1-х — пачатак $date2-х гг.',
bn='$date1-এর দশকের শেষে এবং $date2-এর দশকের শুরুতে',
cs=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "přelom 1. a 2. dekády " .. century1 .. ". století"
elseif decade2 == 0 and date2 < date1 then
return "přelom 9. a 10. dekády " .. century1 .. ". století"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "přelom " .. century1 .. ". a " .. century2 .. ". století"
else
return "přelom " .. decade1 .. "0. a " .. decade2 .. "0. let " .. century1 .. ". století"
end
end,
de='späte $date1er und frühe $date2er',
en='late $date1s and early $date2s',
['en-gb']='late $date1s and early $date2s',
['en-ca']='late $date1s and early $date2s',
eo='ŝanĝo de la $date1-a al la $date2-a jardekoj',
es='finales de los años $date1 y principios de los años $date2',
fa='اواخر دههٔ $date1 و اوایل دههٔ $date2',
fi='$date1-luvun loppu ja $date2-luvun alku',
fr='fin des années $date1 / début des années $date2',
hr='kraj $date1-ih i početak $date2-ih',
hsb='pózdnje $date1te a zažne $date2te lěta',
it='fine degli anni $date1 / inizio degli anni $date2',
ja='$date1年代末から$date2年代初頭',
ml='$date1കൾ അവസാനവും $date2കൾ ആദ്യവും',
nb = 'sent på $date1-tallet og tidlig på $date2-tallet',
nl = 'eind $date1e/begin $date2e decennium',
nn = 'seint på $date1-talet og tidleg på $date2-talet',
pl=function(date1, date2, state)
local decade1 = math.floor(date1 % 100)
local decade2 = math.floor(date2 % 100)
local century1 = Roman(math.floor( date1 / 100) + 1)
local century2 = Roman(math.floor( date2 / 100) + 1)
if decade1 == 0 and date1 < date2 then
return "przełom pierwszej i drugiej dekady " .. century1 .. " wieku"
elseif decade1 == 10 and date1 < date2 then
return "przełom drugiej dekady i lat 20. " .. century1 .. " wieku"
elseif decade1 == 90 and decade2 == 0 then
return "przełom ostatniej dekady " .. century1 .. " i pierwszej dekady " .. century2 .. " wieku"
else
return "przełom lat " .. decade1 .. ". i " .. decade2 .. ". " .. century1 .. " wieku"
end
end,
qu='qhipaq $date1 watakuna ñawpaq $date2 watakunapas',
ru='конец $date1-х — начало $date2-х гг.',
sv='sena $date1-talet och tidiga $date2-talet',
sk=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "prelom 1. a 2. desaťročia " .. century1 .. ". storočia"
elseif decade2 == 0 and date2 < date1 then
return "prelom 9. a 10. desaťročia " .. century1 .. ". storočia"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "prelom " .. century1 .. ". a " .. century2 .. ". storočia"
else
return "prelom " .. decade1 .. "0. a " .. decade2 .. "0. rokov " .. century1 .. ". storočia"
end
end,
sl='pozna $date1-ta in zgodnja $date2-ta leta',
uk='кінець $date1-х — початок $date2-х рр.',
ur='اخیر دہائی $date1 و ابتدا دہائی $date2',
vi='bước sang thập niên $date2',
['zh-hans']='$date1年代末$date2年代初',
['zh-hant']='$date1年代末$date2年代初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='decade', era1=state.era1, adj2 = 'early', unit2='decade', era2=state.era2} end,
},
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the year']={
nDates = 2,
['be-tarask']='канец $date1 — пачатак $date2 г.',
bn='$date1-এর শেষে এবং $date2-এর শুরুতে',
cs='přelom roku $date1/$date2',
de='Jahreswechsel $date1/$date2',
en='late $date1 and early $date2',
['en-gb']='late $date1 and early $date2',
['en-ca']='late $date1 and early $date2',
eo='jarŝanĝo $date1 / $date2',
es='finales de $date1 y principios de $date2',
fa='اواخر $date1 و اوایل $date2',
fi='vuoden $date1 loppu ja vuoden $date2 alku',
fr='fin $date1 / début $date2',
hsb='kónc $date1/spočatk $date2',
ja='$date1年末から$date2年初め',
ml='$date1 അവസാനവും $date2 ആദ്യവും',
nb='årsskiftet $date1/$date2',
nl='eind $date1 en begin $date2',
nn='årsskiftet $date1/$date2',
pl='koniec $date1 i początek $date2 roku',
qu="$date1 qhipaq p'unchawkuna $date2 ñawpaq p'unchawkunapas",
ru='конец $date1 — начало $date2 г.',
sk='prelom rokov $date1/$date2',
sl='konec leta $date1 in začetek $date2',
sv='sena $date1 och tidiga $date2',
uk='кінець $date1 — початок $date2 р.',
ur='اخیر $date1 و ابتدا $date2',
vi='bước sang năm $date2',
['zh-hans']='$date1年末$date2年初',
['zh-hant']='$date1年末$date2年初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='year', era1=state.era1, adj2 = 'early', unit2='year', era2=state.era2} end,
},
['circa2'] = {
nDates = 2,
ar='حوالي $date1 – $date2',
['be-tarask']={'прыкладна з $date1 да прыкладна $date2', case={'gen','gen'}},
bn='প্রায় $date1 এবং প্রায় $date2-এর মাঝামাঝি',
ca='cap a $date1 o $date2',
cs='cca $date1 až $date2',
da='ca. mellem $date1 og $date2',
de='etwa $date1 bis $date2',
el='περίπου μεταξύ $date1 και $date2',
en='between circa $date1 and circa $date2',
['en-gb']='between circa $date1 and circa $date2',
['en-ca']='between circa $date1 and circa $date2',
eo='proksimume de $date1 ĝis $date2',
es='hacia $date1 o $date2',
et='ca. $date1 ja $date2 vahel',
fa='حدوداً بین $date1 و $date2',
fi={function(date1, date2, state) return 'suunnilleen ' .. decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='vers $date1-$date2',
gl='entre $date1 e $date2',
he='בסביבות ה- $date1 עד $date2',
hr='oko između $date1 i $date2',
hsb={'wot něhdźe $date1 do něhdźe $date2', case={'gen','gen'}},
hu='$date1 és $date2 között',
it='tra il $date1 e il $date2 circa',
ja='$date1頃と$date2頃の間',
mk='~ помеѓу $date1 и $date2',
ml='ഉദ്ദേശം $date1-നും ഉദ്ദേശം $date2-നും ഇടയ്ക്ക്',
nb='ca. mellom $date1 og $date2',
nn='ca. mellom $date1 og $date2',
nds='üm un bi $date1 bet $date2',
nl='ca. $date1 tot $date2',
no='ca. mellom $date1 og $date2',
pl={'od około $date1 do około $date2', case={'gen','gen'}},
pt='cerca de $date1 a $date2',
qu="$date1 p'unchawmantachá $date2 p'unchawkamachá",
ro='circa $date1 – $date2',
ru={'с около $date1 до, примерно, $date2', case={'gen','gen'}},
sk='cca $date1 až $date2',
sl={function(date1, date2, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1 do $date2' end, case={'gen','gen'}},
sv='c:a $date1 till $date2',
uk={'з близько $date1 до, приблизно, $date2', case={'gen','gen'}},
ur='قریباً بین $date1 و $date2',
vi='giữa vào khoảng $date1 và vào khoảng $date2',
['zh-hans']='约$date1~$date2间',
['zh-hant']='約$date1~$date2間',
default=function(date1, date2, state) return {conj='between', adj1 = 'circa', unit1=state.unit1, era1=state.era1, adj2 = 'circa', unit2=state.unit2, era2=state.era2} end,
},
-- ==========================================
-- === Eras and calendars ===================
-- ==========================================
['julian'] = {
nDates = 2,
ar='$date1 ($date2 حسب [[w:ar:التقويم اليولياني|تقويم يولياني]])',
['be-tarask']='$date1 ($date2 паводле [[w:be-tarask:Юліянскі каляндар|ст. ст.]]',
bn='$date1 (জুলিয়ান ক্যালেন্ডারে $date2)',
ca='$date1 ($date2 segons el [[w:ca:Calendari julià|calendari julià]])',
cs='$date1 ($date2 podle [[w:cs:Juliánský kalendář|juliánského kalendáře]])',
da='$date1 ($date2 [[w:da:Julianske kalender|Julianske kalender]])',
de='$date1 ($date2 [[w:de:Julianischer Kalender|Julianischer Kalender]])',
el='$date1 ($date2 με το [[w:el:Ιουλιανό ημερολόγιο|Ιουλιανό ημερολόγιο]])',
en='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-gb']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-ca']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
fa='$date1 ($date2 در [[w:fa:Old Style and New Style dates|تقویم جولیان]])',
eo='$date1 ($date2 laŭ la [[w:eo:Julia kalendaro|Julia Kalendaro]])',
es='$date1 ($date2 según el [[w:es:Calendario juliano|calendario juliano]])',
et='$date1 ($date2 [[w:et:Juliuse kalender|Juliuse kalendris]])',
fi='$date1 ($date2 [[w:fi:juliaaninen kalenteri|juliaanisen kalenterin]] mukaan)',
fr='$date1 ($date2 dans le [[w:fr:calendrier julien|calendrier julien]])',
gl='$date1 ($date2 segundo o [[w:gl:Calendario xuliano|calendario xuliano]])',
hu='$date1 ($date2 a [[w:hu:Julianus-naptár|Julián naptárban]])',
hr='$date1 ($date2 prema [[w:hr:Julijanski kalendar|julijanskom kalendaru]])',
he='$date1 ($date2 ב[[w:he:לוח היוליאני|הלוח היוליאני]])',
hsb='$date1 ($date2 po julianiskej protyce)',
it='$date1 ($date2 secondo il [[w:it:Calendario giuliano|calendario giuliano]])',
ja='$date1([[w:ja:ユリウス暦|ユリウス暦]] $date2)',
mk='$date1 ($date2 по [[w:mk:Јулијански календар|Јулијанскиот календар]])',
ml='$date1 ([[w:ml:ജൂലിയൻ കാലഗണനാരീതി|ജൂലിയൻ വർഷത്തിൽ]] $date2)',
ml='$date1 ([[w:Old Style and New Style dates|ജൂലിയൻ കാലഗണനാരീതിയിൽ]] $date2)',
nds='$date1 ($date2 na’n [[w:nds:Juliaansch Klenner|Juliaanschen Klenner]])',
nb='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nn='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nl='$date1 ($date2 volgens de [[w:nl:Juliaanse kalender|Juliaanse kalender]])',
no='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalender]])',
pl='$date1 ($date2 wedle [[w:pl:Daty nowego i starego porządku|kalendarza juliańskiego]])',
pt='$date1 ($date2 no [[w:pt:Mudança para o calendário gregoriano|calendário juliano]])',
qu='$date1 ($date2 [[w:qu:Hulyanu kalindaryu|Hulyanu kalindaryupi]])',
ro='$date1 ($date2 în [[w:ro:calendarul iulian|calendarul iulian]])',
ru='$date1 ($date2 [[w:ru:Юлианский календарь|по ст. ст.]])',
sk='$date1 ($date2 podľa [[w:sk:Juliánsky kalendár|juliánskeho kalendára]])',
sl='$date1 ($date2 po [[w:sl:Julijanski koledar|julijanskem koledarju]])',
sv='$date1 ($date2 [[w:sv:Gamla stilen|gamla stilen]])',
uk='$date1 ($date2 [[w:uk:Юліанський календар|Юліанський календар]])',
ur='$date1 ($date2 حسب [[w:ur:نئے اور پانے انداز کی تاریخ|جولین تقویم]])',
vi='$date1 ($date2 trong [[w:vi:Lịch Julius|lịch Juliêng]])',
['zh-hans']='$date1([[w:Old Style and New Style dates|格里历]]$date2)',
['zh-hant']='$date1([[w:Old Style and New Style dates|格里曆]]$date2)',
default='<span style="color:red">$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])</span>',
},
['ad'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:ад Нараджэньня Хрыста|пасьля Н. Х.]]',
bn ='$date1 খ্রিস্টাব্দ',
ca ='$date1 dC',
cs ='$date1 n. l.',
de ='$date1 n. Chr.',
el ='$date1 μ.Χ.',
en ='$date1 [[w:en:Anno Domini|AD]]',
['en-gb'] ='$date1 [[w:en:Anno Domini|AD]]',
['en-ca'] ='$date1 [[w:en:Anno Domini|AD]]',
eo ='$date1 pK',
es ='$date1 [[w:es:Anno Dómini|d. C.]]',
fa ='$date1 م.',
fi ='$date1 jaa.',
fr ='$date1 ap. J.-C.',
gl ='$date1 d.C.',
he ='$date1 לספירה',
hu ='i. sz. $date1',
hr ='$date1 p. Kr.',
hsb='$date1 po Chr.',
it ='$date1 d.C.',
ja ='西暦$date1',
mk ='$date1 н.е.',
ml ='ക്രി.വ. $date1',
nb ='$date1 e.Kr.',
nl ='$date1 n.Chr.',
nn ='$date1 e.Kr.',
no ='$date1 e.Kr.',
pl ='$date1 [[w:pl:naszej ery|n.e.]]',
ro ='$date1 d.Hr.',
ru ='$date1 н. э.',
sk ='$date1 po Kr.',
sl ='$date1 n. št.',
sv ='$date1 [[w:sv:Efter Kristus|e.Kr.]]',
uk ='$date1 н. е.',
ur ='$date1 ء',
vi ='$date1 [[w:vi:Công Nguyên|CN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元]]$date1',
default='<span style="color:red">$date1 [[w:en:Anno Domini|AD]]</span>',
},
['ah'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:Ісламскі каляндар|хіджры]]',
bn ='$date1 [[w:bn:ইসলামি বর্ষপঞ্জি|হিজরী]]',
cs ='$date1 [[w:cs:Anno Hegirae|AH]]',
en ='$date1 [[w:en:Hijri year|AH]]',
['en-gb'] ='$date1 [[w:en:Hijri year|AH]]',
['en-ca'] ='$date1 [[w:en:Hijri year|AH]]',
eo ='$date1 post la [[w:eo:Heĝiro|Heĝiro]]',
fa ='$date1 ه.ق.',
fr ='$date1 [[w:fr:Calendrier musulman|A.H.]]',
he ="$date1 [[w:he:הלוח המוסלמי|להג'רה]]",
hr ='$date1 god. hidžre',
ja ='[[w:ja:ヒジュラ暦|ヒジュラ暦]]$date1',
ml ='$date1 [[w:ml:ഇസ്ലാമിക കലണ്ടർ|ഹിജ്റ]]',
nb ='$date1 [[w:no:Den muslimske kalenderen|A.H.]]',
nn ='$date1 [[w:nn:Den muslimske kalenderen|A.H.]]',
pl ='$date1 [[w:pl:Kalendarz muzułmański|A.H.]]',
ru ='$date1 [[w:ru:Исламский календарь|хиджры]]',
sk ='$date1 [[w:sk:A. H.|A. H.]]',
sv ='$date1 [[w:sv:Muslimska kalendern|A.H.]]',
uk ='$date1 [[w:uk:Мусульманський календар|хіджри]]',
ur ='$date1 ھ',
vi ='$date1 [[w:vi:Lịch Hồi giáo|AH]]',
['zh-hans'] ='[[w:en:Hijri year|Hijri历]]$date1',
['zh-hant'] ='[[w:en:Hijri year|Hijri曆]]$date1',
default='<span style="color:red">$date1 [[w:en:Hijri year|AH]]</span>',
},
['bc'] = {
nDates = 1,
ar ='$date1 ق.م.',
['be-tarask']='$date1 [[w:be-tarask:да Нараджэньня Хрыста|да Н. Х.]]',
bn ='খ্রিস্টপূর্ব $date1',
ca ='$date1 [[w:ca:Abans de Crist|aC]]',
cs ='$date1 [[w:cs:Př. n. l.|př. n. l.]]',
de ='$date1 [[w:de:v. Chr.|v. Chr.]]',
el ='$date1 π.Χ.',
en ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-gb'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-ca'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
eo ='$date1 aK',
es ='$date1 [[w:es:Antes de Cristo|a. C.]]',
fa ='$date1 پ.م.',
fi ='$date1 eaa.',
fr ='$date1 av. J.-C.',
gl ='$date1 [[w:gl:Antes de Cristo|a.C.]]',
he ='$date1 לפני הספירה',
hr ='$date1 [[w:hr:Pr. Kr.|pr. Kr.]]',
hsb='$date1 do Chr.',
hu ='i. e. $date1',
it ='$date1 [[w:it:Avanti Cristo|a.C.]]',
ja ='[[w:ja:紀元前|紀元前]]$date1',
mk ='$date1 п.н.е.',
ml ='ക്രി.മു. $date1',
nds='$date1 vör uns Tied',
nb ='$date1 f.Kr.',
nl ='$date1 v.Chr.',
nn ='$date1 f.Kr.',
no ='$date1 f.Kr.',
pl ='$date1 [[w:pl:przed naszą erą|p.n.e.]]',
ro ='$date1 î.Hr.',
ru ='$date1 до н. э.',
sk ='$date1 [[w:sk:Pred Kristom|pred Kr.]]',
sl ='$date1 pr. n. št.',
sv ='$date1 [[w:sv:Före Kristus|f.Kr.]]',
uk ='$date1 до н. е.',
ur ='$date1 ق م',
vi ='$date1 [[w:vi:Trước Công Nguyên|TCN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元前]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元前]]$date1',
default='<span style="color:red">$date1 [[w:en:Ante Christum Natum|BC]]</span>',
},
['bp'] = {
nDates = 1,
ar ='$date1 [[w:ar:قبل الحاضر|قبل الحاضر]]',
be ='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
['be-tarask']='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
bn ='বর্তমানের আগে $date1',
ca ='$date1 [[w:ca:BP (unitat de temps)|BP]]',
cs ='$date1 [[w:cs:B. P.|B. P.]]',
de ='$date1 [[w:de:Before Present|BP]]',
el ='$date1 [[w:en:Before Present|BP]]',
en ='$date1 [[w:en:Before Present|BP]]',
['en-gb'] ='$date1 [[w:en:Before Present|BP]]',
['en-ca'] ='$date1 [[w:en:Before Present|BP]]',
eo='$date1 [[w:eo:Before Present|BP]]',
es ='$date1 [[w:es:Before Present|BP]]',
et ='$date1 [[w:et:Before Present|BP]]',
fa ='$date1 پیش از اکنون',
fi ='$date1 [[w:fi:BP (ajanlasku)|BP]]',
fr ='$date1 [[w:fr:Avant le présent|avant le présent]]',
hr ='$date1 [[w:hr:BP|prije današnjice]]',
it ='$date1 [[w:it:Before Present|BP]]',
ja ='$date1 [[w:ja:BP (年代測定)|BP]]',
lv ='$date1 [[w:lv:Pirms mūsdienām|Pirms mūsdienām]]',
mk ='$date1 [[w:mk:Пред сегашноста|п.с.]]',
ml ='[[w:en:Before Present|ഇന്നത്തെ കാലത്തിനു മുമ്പ്]] $date1',
nb ='$date1 [[w:no:Before Present|BP]]',
nn ='$date1 [[w:nn:Before Present|BP]]',
nl ='$date1 [[w:nl:Before Present|BP]]',
no ='$date1 [[w:no:Before Present|BP]]',
pl ='$date1 [[w:pl:Before Present|BP]]',
pt ='$date1 [[w:pt:Antes do Presente|Antes do Presente]]',
ru ='$date1 [[w:ru:До настоящего времени|до н. в.]]',
sk ='$date1 [[w:sk:BP (čas)|BP]]',
sl ='$date1 [[w:sl:pred sedanjostjo|p. s.]]',
sr ='$date1 [[w:sr:Прије садашњости|Прије садашњости]]',
sv ='$date1 [[w:sv:B.P.|B.P.]]',
uk ='$date1 [[w:uk:Років тому|Років тому]]',
ur ='$date1 قبل حاضر',
vi ='$date1 [[w:en:Before Present|trước ngày nay]]',
['zh-hans'] ='[[w:zh:距今|距今]]$date1',
['zh-hant'] ='[[w:zh:距今|距今]]$date1',
default='<span style="color:red">$date1 [[w:en:Before Present|BP]]</span>',
},
}
-- List of possible string inputs to "adj", "junction", "era", and "precision" fields
-- Each term is matched to its synonym and the role: a - adjective, e - era
-- p - precision, c - calendar and combos, r - certainty and j - conjunction
p.Synonyms = {
-- adjectives and prepositions
['quarter1']={'1quarter','a'},
['1stquarter']={'1quarter','a'},
['1st quarter']={'1quarter','a'},
['first quarter']={'1quarter','a'},
['1quarter']={'1quarter','a'},
['quarter2']={'2quarter','a'},
['2ndquarter']={'2quarter','a'},
['2nd quarter']={'2quarter','a'},
['second quarter']={'2quarter','a'},
['2quarter']={'2quarter','a'},
['quarter3']={'3quarter','a'},
['3rdquarter']={'3quarter','a'},
['3rd quarter']={'3quarter','a'},
['third quarter']={'3quarter','a'},
['3quarter']={'3quarter','a'},
['quarter4']={'4quarter','a'},
['4thquarter']={'4quarter','a'},
['4th quarter']={'4quarter','a'},
['fourth quarter']={'4quarter','a'},
['4quarter']={'4quarter','a'},
['spring']={'spring','a'},
['summer']={'summer','a'},
['fall']={'autumn','a'},
['autumn']={'autumn','a'},
['winter']={'winter','a'},
['beginning']={'early','a'},
['early']={'early','a'},
['1half']={'firsthalf','a'},
['1sthalf']={'firsthalf','a'},
['1st half']={'firsthalf','a'},
['firsthalf']={'firsthalf','a'},
['first half']={'firsthalf','a'},
['end']={'late','a'},
['late']={'late','a'},
['middle']={'mid','a'},
['mid']={'mid','a'},
['2half']={'secondhalf','a'},
['2ndhalf']={'secondhalf','a'},
['2nd half']={'secondhalf','a'},
['secondhalf']={'secondhalf','a'},
['second half']={'secondhalf','a'},
['<']={'before','a'},
['b']={'before','a'},
['before']={'before','a'},
['>']={'after','a'},
['a']={'after','a'},
['after']={'after','a'},
['~']={'circa','a'},
['c']={'circa','a'},
['ca']={'circa','a'},
['circa']={'circa','a'},
['by']={'by','a'},
['from']={'from','a'},
['until']={'until','a'},
['uncertain']={'uncertain','r'},
['possibly']={'possibly','r'},
['probably']={'probably','r'},
['presumably']={'presumably','r'},
-- eras
['bc']={'bc','e'},
['bce']={'bc','e'},
['before present']={'bp','e'},
['bp']={'bp','e'},
['ce']={'ad','e'},
['ad']={'ad','e'},
['ah']={'ah','e'},
-- conjunctions
['&']={'and','j'},
['and']={'and','j'},
['or']={'or','j'},
['-']={'from-until','j'},
['–']={'from-until','j'},
['from-until']={'from-until','j'},
['between']={'between','j'},
['circa2']={'circa2','a'},
-- precision
['day']={'day','p'},
['month']={'month','p'},
['year']={'year','p'},
['s']={'decade','p'},
['decade']={'decade','p'},
['century']={'century','p'},
['millennium']={'millennium','p'},
['million']={'million','p'},
['billion']={'billion','p'},
-- calendar
['julian']={'julian','c'},
['islamic']={'islamic','c'},
['hijri']={'islamic','c'},
-- "Turn of the ..." options are a combos which will be handled in similar way as calendars
['turn of the century']={'turn of the century','c'},
['century turn']={'turn of the century','c'},
['turn of the decade']={'turn of the decade','c'},
['decade turn']={'turn of the decade','c'},
['turn of the year']={'turn of the year','c'},
['year turn']={'turn of the year','c'},
-- other dates which will be handled in similar way as calendars
['?']={'unknown','c'},
['unknown']={'unknown','c'},
['unknown year']={'year unknown','c'},
['year unknown']={'year unknown','c'},
}
return p
a3jszs2h8krglns3fs0hdwxtma3dco2
803610
803609
2019-07-28T14:17:09Z
en>RexxS
0
Add ckb functionality per edit request - copied from [[:ckb:Module:I18n/complex date]] 28 July 2019
803610
Scribunto
text/plain
--[[
__ __ _ _ ___ _ ___ __ _ _ _
| \/ | ___ __| |_ _| | ___ _|_ _/ |( _ ) _ __ / /__ ___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_)| || |/ _ \| '_ \ / / __/ _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_ | || | (_) | | | |/ / (_| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)___|_|\___/|_| |_/_/ \___\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This code was copied from Commons please request changes there.
Maintainers:
*Jarekt
]]
local function Ordinal(...)
return require('Module:Ordinal-cd')._Ordinal(...)
end
local function Roman(...)
return require('Module:Roman-cd')._Numeral(...)
end
-- ==========================================================================
-- === Local Helper functions ==============================================
-- ==========================================================================
-- form of French "of" based on "precision", which is wikibase compatible date precision format:
-- 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- 1 2 3 4 5 mill cent deca year month day
local frOf = {'du','du','du','du','du', 'du', 'du', 'de', 'de', 'de', 'du'}
local function decYear(state,lang,case)
--Declension of word "year" in several languages
if state.precision == 9 then
yearCase = {
fi={xxx='vuosien', gen='vuoden', ill='vuoteen', ptv='vuotta', ela='vuodesta', ine='vuonna'},
sl={insP='letoma', ins='letom', gen='leta', loc='letu'},
hsb={insP='lětami', ins='lětom', gen='lěta', loc='lěće'}
}
return yearCase[lang][case] .. ' '
else
return ''
end
end
local function decCentury(lang,case)
--Declension of word "century" in several languages
centuryCase = {
pl={nom='wiek', gen='wieku', ins='wiekiem', loc='wieku', default = 'wiek'},
hsb={nom='lětstotk', gen='lětstotka', ins='lětstotkom', loc='lětstotku', default = 'lětstotk'}
}
return centuryCase[lang][case] or centuryCase[lang]['default']
end
local function decMillennium(lang,case)
--Declension of word "century" in several languages
millenniumCase = {
pl={nom='tysiąclecie', gen='tysiąclecia', ins='tysiącleciem', loc='tysiącleciu', default = 'tysiąclecie'},
hsb={nom='lěttysac', gen='lěttysaca', ins='lěttysacom', loc='lěttysacu', default = 'lěttysac'}
}
return millenniumCase[lang][case] or millenniumCase[lang]['default']
end
-- ==========================================================================
-- === Output data structure ===============================================
-- ==========================================================================
local p = {}
--[[
Multiple formats are possible:
1) string with key words "$date1" and "$date2" which will be replaced later by the date strings
2) function which can be used to create output string
* Function INPUTS:
- date1 and date2 string with date strings. "$date1" and "$date2" can also be used which will be replaced latter
- state is a table with full description of the phrase we are trying to construct. It includes state.precision1 (and 2)
which is wikibase compatible date precision format: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
3) table with following fields:
* table[1] is either string or a function
* table.case is a 2 element table with Grammatical cases used by given language to alter
month name or other adjective related to date # 1 and 2
]]
p.Translations = {
-- ==========================================
-- === Two date phrases =====================
-- ==========================================
['from-until'] = {
nDates = 2,
ar='من $date1 إلى $date2',
['be-tarask']={'з $date1 да $date2', case={'gen','gen'}},
bn='$date1 থেকে $date2 পর্যন্ত',
ca='$date1 fins $date2',
ckb='لە $date1 تاوەکوو $date2',
cs='$date1 až $date2',
da='fra $date1 til $date2',
de='$date1 bis $date2',
el='από $date1 έως $date2',
en='from $date1 until $date2',
['en-gb']='from $date1 until $date2',
['en-ca']='from $date1 until $date2',
eo='de $date1 al $date2',
es='de $date1 a $date2',
et='$date1 kuni $date2',
fa='از $date1 تا $date2',
fi='$date1–$date2', -- välisenä aikana
fr=function(date1, date2, state) -- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision > 10 then -- precision > month
return 'du $date1 au $date2'
else -- "DE septebmbre" / "D'octobre"
return require("Module:Linguistic").of(date1, 'fr') .. " à $date2"
end
end,
gl='de $date1 a $date2', --Template:From until used 'desde $date1 ata $date2'
he='מ- $date1 עד $date2',
hr='od $date1 do $date2',
hsb={'wot $date1 do $date2', case={'gen','gen'}},
hu='$date1 – $date2',
it='dal $date1 al $date2',
ja='$date1から$date2まで',
mk='од $date1 до $date2',
ml='$date1 മുതൽ $date2 വരെ',
nb='fra $date1 til $date2',
nds='$date1 bet $date2',
nn='fra $date1 til $date2',
nl='$date1 tot $date2',
no='fra $date1 til $date2',
pl={'od $date1 do $date2', case={'gen','gen'}},
ro='din $date1 până în $date2',
pt='de $date1 a $date2',
qu="$date1 p'unchawmanta $date2 p\'unchawkama",
ru={'с $date1 по $date2', case={'gen','gen'}},
sk={'od $date1 do $date2', case={'gen','gen'}},
sl={'od $date1 do $date2', case={'gen','gen'}},
sv='från $date1 till $date2',
uk={'з $date1 до $date2', case={'gen','gen'}},
ur='از $date1 تا $date2',
vi='từ $date1 đến $date2',
['zh-hans']='从$date1到$date2',
['zh-hant']='從$date1到$date2',
default='<span style="color:red">$date1 - $date2</span>',
},
['between']={
nDates = 2,
ar='بين $date1 و $date2',
['be-tarask']={'між $date1 і $date2', case={'ins','ins'}},
bn='$date1 এবং $date2-এর মাঝামাঝি',
ca='entre $date1 i $date2',
ckb='لەنێوان $date1 و $date2',
cs={'mezi $date1 a $date2', case={'ins','ins'}},
da='mellem $date1 og $date2',
de='zwischen $date1 und $date2',
el='μεταξύ $date1 και $date2',
en='between $date1 and $date2',
['en-gb']='between $date1 and $date2',
['en-ca']='between $date1 and $date2',
eo='inter $date1 kaj $date2',
es='entre $date1 y $date2',
et='$date1 ja $date2 vahel',
fa='بین $date1 و $date2',
fi={function(date1, date2, state) return decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='entre $date1 et $date2',
gl='entre $date1 e $date2',
he='בין $date1 ו- $date2',
hr='između $date1 i $date2',
hsb={'mjez $date1 a $date2', case={'ins','ins'}},
hu='$date1 és $date2 között',
hi='$date1 और $date2 के बीच',
it='tra il $date1 e il $date2',
ja='$date1と$date2の間',
mk='помеѓу $date1 и $date2',
ml='$date1, $date2 എന്നിവയ്ക്കിടയ്ക്ക്',
nb='mellom $date1 og $date2',
nds='twischen $date1 un $date2',
nn='mellom $date1 og $date2',
nl='tussen $date1 en $date2',
no='mellom $date1 og $date2',
pl={'między $date1 a $date2', case={'ins','ins'}},
ro='între $date1 și $date2',
pt='entre $date1 e $date2',
qu="$date1 p'unchawpa $date2 p'unchawpapas chawpin",
ru={'между $date1 и $date2', case={'ins','ins'}},
sk={'medzi $date1 a $date2', case={'ins','ins'}},
sl={function(date1, date2, state) return 'med ' .. decYear(state,'sl','insP') .. '$date1 in $date2' end, case={'ins','ins'}},
sv='mellan $date1 och $date2',
uk={'між $date1 та $date2', case={'ins','ins'}},
ur='بین $date1 و $date2',
vec='tra el $date1 e el $date2',
vi='giữa $date1 và $date2',
['zh-hans']='在$date1到$date2之间',
['zh-hant']='在$date1到$date2之間',
default='<span style="color:red">$date1 - $date2</span>',
},
['or'] = {
nDates = 2,
ar='$date1 أو $date2',
['be-tarask']='$date1 ці $date2',
bn='$date1 অথবা $date2',
ca='$date1 o $date2',
ckb='$date1 یان $date2',
cs='$date1 nebo $date2',
da='$date1 eller $date2',
de='$date1 oder $date2',
el='$date1 ή $date2',
en='$date1 or $date2',
['en-gb']='$date1 or $date2',
['en-ca']='$date1 or $date2',
eo='$date1 aŭ $date2',
es='$date1 o $date2',
et='$date1 või $date2',
fa='$date1 یا $date2',
fi='$date1 tai $date2',
fr='$date1 ou $date2',
gl='$date1 ou $date2',
he='$date1 או $date2',
hr='$date1 ili $date2',
hsb='$date1 abo $date2',
hu='$date1 vagy $date2',
it='$date1 o $date2',
ja='$date1または$date2',
mk='$date1 или $date2',
ml='$date1 അഥവാ $date2',
nb='$date1 eller $date2',
nn='$date1 eller $date2',
nds='$date1 oder $date2',
nl='$date1 of $date2',
no='$date1 eller $date2',
pl='$date1 lub $date2',
pt='$date1 ou $date2',
qu='$date1 icha $date2',
ro='$date1 sau $date2',
ru='$date1 или $date2',
sk='$date1 alebo $date2',
sl='$date1 ali $date2',
sv='$date1 eller $date2',
uk='$date1 або $date2',
ur='$date1 یا $date2',
th='$date1 หรือ $date2',
vi='$date1 hoặc $date2',
['zh-hans']='$date1或$date2',
['zh-hant']='$date1或$date2',
default=function(date1, date2, state) return require("Module:linguistic").conj({date1, date2}, state.lang, 'or') end,
},
['and'] = {
nDates = 2,
ar='$date1 و $date2',
['be-tarask']='$date1 і $date2',
bn='$date1 এবং $date2',
ca='$date1 i $date2',
ckb='$date1 و $date2',
cs='$date1 a $date2',
da='$date1 og $date2',
de='$date1 und $date2',
el='$date1 και $date2',
en='$date1 and $date2',
['en-gb']='$date1 and $date2',
['en-ca']='$date1 and $date2',
eo='$date1 kaj $date2',
es='$date1 y $date2',
et='$date1 ja $date2',
fa='$date1 و $date2',
fi='$date1 ja $date2',
fr='$date1 et $date2',
gl='$date1 e $date2',
he='$date1 ו- $date2',
hu='$date1 és $date2',
hsb='$date1 a $date2',
it='$date1 e $date2',
ja='$date1および$date2',
mk='$date1 и $date2',
ml='$date1 ഒപ്പം $date2',
nb='$date1 og $date2',
nn='$date1 og $date2',
nds='$date1 un $date2',
nl='$date1 en $date2',
no='$date1 og $date2',
pl='$date1 i $date2',
pt='$date1 e $date2',
qu='$date1wan $date2pas',
ro='$date1 și $date2',
ru='$date1 и $date2',
sk='$date1 a $date2',
sl='$date1 in $date2',
sv='$date1 och $date2',
uk='$date1 та $date2',
ur='$date1 و $date2',
vi='$date1 và $date2',
['zh-hans']='$date1和$date2',
['zh-hant']='$date1和$date2',
default=function(date1, date2, state) return require("Module:linguistic").conj({date1, date2}, state.lang, 'and') end,
},
-- ==========================================
-- === Prepositions =========================
-- ==========================================
['by'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'да $date1', case={'gen',''}},
bn='$date1 কর্তৃক',
ca='vers $date1',
ckb='نزیکی $date1',
cs={'kolem $date1', case={'gen',''}},
da='mod $date1',
de='spätestens $date1',
el='έως $date1',
en='by $date1',
['en-gb']='by $date1',
['en-ca']='by $date1',
eo='ne post $date1',
es='para $date1',
et='hiljemalt $date1',
fa='توسط $date1',
fi={function(date1, state) return decYear(state,'fi','ill') .. '$date1 mennessä' end, case={'ill',''}},
fr='$date1 (au plus tard)',
gl='cara a $date1',
he='עד $date1',
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='entro il $date1',
ja='$date1まで',
mk='кон $date1',
ml='$date1-ൽ',
nb='innen $date1',
nn='innan $date1',
nds='$date1 oder vördem al',
nl='tegen $date1',
no='innen $date1',
pl={'przed $date1', case={'ins',''}},
pt='por $date1',
qu="$date1 p'unchawkama",
ro='de $date1',
ru={'к $date1', case={'dat',''}},
sk={'okolo $date1', case={'gen',''}},
sl={function(date1, state) return 'najpozneje ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='senast $date1',
uk={'до $date1', case={'dat',''}},
ur='توسط $date1',
vi='vào $date1', -- need to insert "đã" somewhere later in the sentence -->
['zh-hans']='$date1前后',
['zh-hant']='$date1前后',
default='<span style="color:red">by $date1</span>',
},
['before'] = {
nDates = 1,
ar='قبل $date1',
['be-tarask']={'перад $date1', case={'ins',''}},
bn='$date1-এর আগে',
bg='Преди $date1',
ca='abans $date1',
ckb='پێش $date1',
cs={'před $date1', case={'ins',''}},
da='før $date1',
de='vor $date1',
el='πριν από $date1',
en='before $date1',
['en-gb']='before $date1',
['en-ca']='before $date1',
eo='antaŭ $date1',
es='antes de $date1',
et='enne $date1',
fa='پیش از $date1',
fi={function(date1, state) return 'ennen ' .. decYear(state,'fi','ptv') .. '$date1' end, case={'ptv',''}},
fr='avant $date1',
gl='antes do $date1',
he='לפני $date1',
hr='prije $date1',
hsb={'do $date1', case={'gen',''}},
hu='$date1 előtt',
it='prima del $date1',
ja='$date1より前',
mk='пред $date1',
ml='$date1-നു മുമ്പ്',
nb='før $date1',
nn='før $date1',
nds='vör $date1',
nl='voor $date1',
no='før $date1',
pl={'przed $date1', case={'ins',''}},
pt='antes de $date1',
qu="$date1 p'unchaw ñawpan",
ro='înainte de $date1',
ru={'ранее $date1', case={'gen',''}},
sk={'pred $date1', case={'ins',''}},
sl={function(date1, state) return 'pred ' .. decYear(state,'sl','ins') .. ' $date1' end, case={'ins',''}},
sv='före $date1',
uk={'до $date1', case={'gen',''}},
ur='قبل $date1',
vi='trước $date1',
['zh-hans']='在$date1之前',
['zh-hant']='在$date1之前',
default='<span style="color:red">before $date1</span>',
},
['from'] = {
nDates = 1,
ar='من $date1',
['be-tarask']={'з $date1', case={'gen',''}},
bn='$date1 থেকে',
ckb='لە $date1',
cs={'od $date1', case={'gen',''}},
de='Seit $date1',
el='από $date1',
en='from $date1',
['en-gb']='from $date1',
['en-ca']='from $date1',
eo='ekde $date1',
es='desde $date1',
et='alates $date1',
fa='از $date1',
fi={function(date1, state) return decYear(state,'fi','ela') .. '$date1 alkaen' end, case={'ela',''}},
fr= function(date1, state) return 'à partir '.. frOf[state.precision] ..' $date1' end,
gl='desde o $date1',
he='מ- $date1',
hr='od $date1',
hsb={'wot $date1', case={'gen',''}},
it='dal $date1',
ja='$date1から',
nds='vonaf $date1',
mk='од $date1',
nb='fra $date1',
nn='frå $date1',
ml='$date1 മുതൽ',
nl='vanaf $date1',
no='fra $date1',
pl={'od $date1', case={'gen',''}},
qu="$date1 p'unchawmanta",
ro='din $date1',
ru={'С $date1', case={'gen',''}},
sk={'od $date1', case={'gen',''}},
sl={function(date1, state) return 'od ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='från $date1',
uk={'з $date1', case={'gen',''}},
ur='از $date1',
vi='từ $date1',
['zh-hans']='自$date1',
['zh-hant']='自$date1',
default='<span style="color:red">from $date1</span>',
},
['until'] = {
nDates = 1,
['be-tarask']={'усутыч да $date1', case={'gen',''}},
bn='$date1 পর্যন্ত',
ckb='تاوەکوو $date1',
cs={'do $date1', case={'gen', ''}},
de='bis $date1',
en='until $date1',
['en-gb']='until $date1',
['en-ca']='until $date1',
eo='ĝis $date1',
es='hasta $date1',
fr=function(date1, state)
if state.precision > 10 then -- precision > month
return "jusqu'au $date1"
else
return "jusqu'à $date1"
end
end,
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='al $date1',
nb='til $date1',
nl='tot $date1',
nn='til $date1',
ml='$date1 വരെ',
pl={'do $date1', case={'gen',''}},
ru={'вплоть до $date1', case={'gen',''}},
sk={'do $date1', case={'gen', ''}},
uk={'аж до $date1', case={'gen',''}},
ur='تک $date1',
['zh-hans']='直到$date1',
['zh-hant']='直到$date1',
default='<span style="color:red">until $date1</span>',
},
['after'] = {
nDates = 1,
ar='بعد $date1',
['be-tarask']={'пасьля $date1', case={'gen',''}},
bn='$date1-এর পরে',
ca='després de $date1',
ckb='دوای $date1',
cs={'po $date1', case={'loc',''}},
de='nach $date1',
da='efter $date1',
el='μετά από $date1',
en='after $date1',
['en-gb']='after $date1',
['en-ca']='after $date1',
eo='post $date1',
es='después de $date1',
et='pärast $date1',
fa='پس از $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 jälkeen' end, case={'gen',''}},
fr='après $date1',
gl='despois do $date1',
he='לאחר $date1',
hr='nakon $date1',
hu='$date1 után',
hsb={'po $date1', case={'loc',''}},
it='dopo il $date1',
ja='$date1より後',
mk='по $date1',
ml='$date1-നു ശേഷം',
nb='etter $date1',
nn='etter $date1',
nds='na $date1',
nl='na $date1',
no='etter $date1',
pl={'po $date1', case={'loc',''}},
pt='após $date1',
qu="$date1 p'unchaw qhipan",
ro='după $date1',
ru={'после $date1', case={'gen',''}},
sk={'po $date1', case={'loc',''}},
sl={function(date1, state) return 'po ' .. decYear(state,'sl','loc') .. ' $date1' end, case={'loc',''}},
sv='efter $date1',
uk={'після $date1', case={'gen',''}},
ur='بعد $date1',
vi='sau $date1',
['zh-hans']='在$date1之后',
['zh-hant']='在$date1之後',
default='<span style="color:red">after $date1</span>',
},
-- ==========================================
-- === Adjectives ===========================
-- ==========================================
['early'] = {
nDates = 1,
ar='بداية $date1',
['be-tarask']={'пачатак $date1', case={'gen',''}},
bn='$date1-এর শুরুতে',
ca='al principi de $date1',
ckb='سەرەتای $date1',
cs={'začátek $date1', case={'gen',''}},
da='tidlig $date1',
de='Anfang $date1',
el='αρχές του $date1',
en='early $date1',
['en-gb']='early $date1',
['en-ca']='early $date1',
eo='komence de $date1',
es='a principios de $date1',
et='$date1 algus',
fa='اوایل $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alku' end, case={'gen',''}},
fr='début $date1',
gl='a principios de $date1',
he='תחילת $date1',
hr='početak $date1',
hsb={'spočatk $date1', case={'gen',''}},
hu='$date1 elején',
it='inizio del $date1',
ja='$date1初頭',
mk='почеток на $date1',
ml='$date1 ആദ്യപാദം',
nb='tidlig $date1',
nn='tidleg $date1',
nds='Anfang $date1',
nl='begin $date1',
no='tidlig $date1',
pl={'początek $date1', case={'gen',''}},
pt='a princípios de $date1',
qu="$date1 ñawpaq p'unchawkunapi",
ro='începând cu $date1',
ru={'начало $date1', case={'gen',''}},
sk={'začiatok $date1', case={'gen',''}},
sl=function(date1, state)-- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision == 9 then -- precision > month
return 'zgodnje leto $date1'
else -- "DE septebmbre" / "D'octobre"
return 'zgodnji $date1'
end
end,
sv='början av $date1',
uk={'початок $date1', case={'gen',''}},
ur='ابتدا $date1',
vi='đầu năm $date1',
['zh-hans']='$date1早期',
['zh-hant']='$date1早期',
default='<span style="color:red">early $date1</span>',
},
['mid'] = {
nDates = 1,
ar='منتصف $date1',
['be-tarask']={'сярэдзіна $date1', case={'gen',''}},
bn='$date1-এর মাঝামাঝি',
ca='a mitjan $date1',
ckb='ناوەڕاستی $date1',
cs={'polovina $date1', case={'gen',''}},
da='midten af $date1',
de='Mitte $date1',
el='μέσα του $date1',
en='mid $date1',
['en-gb']='mid $date1',
['en-ca']='mid $date1',
eo='meze de $date1',
es='a mediados de $date1',
et='$date1 keskel',
fa='اواسط $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 puoliväli' end, case={'gen',''}},
fr='mi $date1',
gl='a mediados de $date1',
he='אמצע $date1',
hr='sredina $date1',
hsb={'srjedź $date1', case={'gen',''}},
hu='$date1 közepén',
it='metà del $date1',
ja='$date1半ば',
mk='средина на $date1',
ml='$date1 മദ്ധ്യം',
nb='midten av $date1',
nn='midten av $date1',
nds='Midd $date1',
nl='midden $date1',
no='midten av $date1',
pl={'środek $date1', case={'gen',''}},
pt='meados de $date1',
qu="$date1 chawpin p'unchawkunapi",
ro='mijlocul lui $date1',
ru={'середина $date1', case={'gen',''}},
sk={'polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'sredina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='mitten av $date1',
uk={'середина $date1', case={'gen',''}},
ur='وسط $date1',
vi='giữa năm $date1',
['zh-hans']='$date1中期',
['zh-hant']='$date1中期',
default='<span style="color:red">mid $date1</span>',
},
['late'] = {
nDates = 1,
ar='نهاية $date1',
bn='$date1-এর শেষে',
['be-tarask']={'канец $date1', case={'gen',''}},
ca='al final de $date1',
ckb='کۆتاییی $date1',
cs='konec $date1',
da='sen $date1',
de='Ende $date1',
el='τέλη του $date1',
en='late $date1',
['en-gb']='late $date1',
['en-ca']='late $date1',
eo='fine de $date1',
es='a fines de $date1',
et='$date1 lõpp',
fa='اواخر $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppu' end, case={'gen',''}},
fr='fin $date1',
gl='a finais de $date1',
he='סוף $date1',
hr='kraj $date1',
hsb={'kónc $date1', case={'gen',''}},
hu='$date1 végén',
it='fine del $date1',
ja='$date1末',
mk='крајот на $date1',
ml='$date1 അന്ത്യപാദം',
nb='sent $date1',
no='seint $date1',
nds='Enn $date1',
nl='eind $date1',
no='sent $date1',
pl={'koniec $date1', case={'gen',''}},
pt='ao final de $date1',
qu="$date1 qhipaq p'unchawkunapi",
ro='sfârșitul lui $date1',
ru={'конец $date1', case={'gen',''}},
sk={'koniec $date1', case={'gen',''}},
sl={function(date1, state) return 'pozno ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='slutet av $date1',
uk={'кінець $date1', case={'gen',''}},
ur='اخیر $date1',
vi='cuối năm $date1',
['zh-hans']='$date1晚期',
['zh-hant']='$date1晚期',
default='<span style="color:red">late $date1</span>',
},
['circa'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'каля $date1', case={'gen',''}},
bn='প্রায় $date1',
ckb='بەنزیکی $date1',
ca='cap a $date1',
cs='cca $date1',
da='ca. $date1',
de='etwa $date1',
el='περ. $date1',
en='circa $date1',
['en-gb']='circa $date1',
['en-ca']='circa $date1',
eo='ĉirkaŭ $date1',
es='hacia $date1', -- suggestion use "hacia"/"hacia el" or leave the latin circa or ca.
et='ca. $date1',
fa='حوالی $date1', -- template:Circa used 'حدود $date1'
fi={function(date1, state) return 'suunnilleen ' .. decYear(state,'fi','ine') .. '$date1' end, case={'ine',''}}, -- template:Circa used 'noin $date1'
fr=function(date1, state)
if state.precision >= 11 then -- precision >= day
return 'vers le $date1'
else
return 'vers $date1'
end
end,
gl='entre $date1', -- template:Circa used 'cara a $date1'
he='בסביבות $date1',
hr='oko $date1',
hsb={'wokoło $date1', case={'gen',''}},
hu='$date1 körül',
it='$date1 circa',
ja='$date1頃',
ko='$date1경',
mk='~ $date1', -- template:Circa used 'o. $date1'
ml= 'ഉദ്ദേശം $date1',
nb='ca. $date1',
nn='ca. $date1',
nds='üm un bi $date1',
nl='ca. $date1',
no='ca. $date1',
pl={'około $date1', case={'gen',''}},
pt='cerca de $date1',
qu='$date1chá',
ro='circa $date1',
ru={'около $date1', case={'gen',''}},
sk='cca $date1',
sl={function(date1, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='c:a $date1', -- template:Circa used 'ca $date1'
uk={'близько $date1', case={'gen', ''}},
ur='قریباً $date1',
vi='vào khoảng $date1',
['zh-hans']='约$date1',
['zh-hant']='約$date1',
default='<span style="color:red">circa $date1</span>',
},
['uncertain'] = {
nDates = 1,
['zh-hans']='$date1(?)',
['zh-hant']='$date1(?)',
default='$date1 (?)',
},
probably = { -- see Q56644435
nDates = 1,
ast = 'probablemente $date1',
bn = 'সম্ভবত $date1',
ca = 'probablement $date1',
ckb = 'لەوانەیە $date1',
cs = 'pravděpodobně $date1',
de = 'wahrscheinlich $date1',
el = 'πιθανόν $date1',
en = 'probably $date1',
['en-gb']='probably $date1',
['en-ca']='probably $date1',
eo = 'probable $date1',
es = 'probable $date1',
fa = 'احتمالاً $date1',
fr = 'probablement $date1',
he = 'כפי הנראה $date1',
hu = 'feltehetőleg $date1',
it = 'probabile $date1',
mk = 'веројатно $date1',
ml = 'ഒരുപക്ഷേ $date1',
nl = 'waarschijnlijk $date1',
pl = 'prawdopodobnie $date1',
pt = 'provavelmente $date1',
ru = 'вероятно $date1',
sv = 'antagligen $date1',
uk = 'ймовірно $date1',
default='<span style="color:red">probably $date1</span>',
},
possibly = { -- see Q30230067
nDates = 1,
ast = 'posiblemente $date1',
bn = 'যথাসম্ভব $date1',
ckb = 'لەوانەیە $date1',
cs = 'možná $date1',
de = 'möglicherweise $date1',
en = 'possibly $date1',
['en-gb']='possibly $date1',
['en-ca']='possibly $date1',
eo = 'eble $date1',
el = 'πιθανόν $date1',
fa = 'احتمالاً $date1',
fr = 'peut-être $date1',
hu = 'talán $date1',
mk = 'можеби $date1',
ml = 'മിക്കവാറും $date1',
nl = 'mogelijk $date1',
pl = 'być może $date1',
pt = 'possivelmente $date1',
ro = 'poate $date1',
ru = 'возможно $date1',
sv = 'möjligen $date1',
uk = 'можливо $date1',
default='<span style="color:red">possibly $date1</span>',
},
presumably = { -- see Q18122778
nDates = 1,
ast = 'presumiblemente $date1',
ckb = 'دەشێت $date1',
de = 'vermutlich $date1',
en = 'presumably $date1',
['en-gb']='presumably $date1',
['en-ca']='presumably $date1',
fr = 'présumé $date1',
it = 'presumibilmente $date1',
ml = 'ഊഹപ്രകാരം $date1',
ru = 'предположительно $date1',
pl = 'przypuszczalnie $date1',
uk = 'здогадно $date1',
default='<span style="color:red">presumably $date1</span>',
},
['year unknown']= {
nDates = 0,
ca='Any desconegut',
['be-tarask']='Год невядомы',
bn='অজানা বছর',
ckb='ساڵی نەزاندراو',
cs='neznámý rok',
de='Jahr unbekannt',
el='άγνωστο έτος',
en='year unknown',
['en-gb']='year unknown',
['en-ca']='year unknown',
eo='jaro nesciata',
es='Año desconocido',
fa='سال نامعلوم',
fi='tuntematon vuosi',
fr='Année inconnue',
he='שנה לא ידועה',
hsb='lěto njeznate',
hr='nepoznata godina',
it='anno sconosciuto',
ja='年代不明',
la='Sine anno',
ml='വർഷം ലഭ്യമല്ല',
mk='непозната година',
nb='ukjent år',
nn='ukjent år',
nl='zonder jaar',
no='ukjent år',
pl='rok nieznany',
qu='Mana riqsisqa wata',
ru='Год неизвестен',
sk='neznámy rok',
sl='neznano leto',
sv='Okänt år',
uk='рік невідомий',
ur='سال نامعلوم',
vi='không rõ năm',
['zh-hans']='年份未知',
['zh-hant']='年份未知',
default='<span style="color:red">year unknown</span>',
},
-- ==========================================
-- === Parts of a whole =====================
-- ==========================================
['firsthalf'] = {
nDates = 1,
ar='النصف الأول من $date1',
['be-tarask']={'першая палова $date1', case={'gen',''}},
bn='$date1-এর প্রথমার্ধ',
ca='1a meitat de $date1',
ckb='نیوەی یەکەمی $date1',
cs={'1. polovina $date1', case={'gen',''}},
da='1. halvdel af $date1',
de='1. Hälfte $date1',
el='πρώτο μισό του $date1',
en='first half of $date1',
['en-gb']='first half of $date1',
['en-ca']='first half of $date1',
eo='unua duono de $date1',
es='primera mitad de $date1',
et='$date1 esimene pool',
fa='نیمهٔ نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alkupuolisko' end, case={'gen',''}},
fr='1<sup>re</sup> moitié de $date1',
gl='primeira metade de $date1',
he='במחצית הראשונה של $date1',
hsb={'prěnja połojca $date1', case={'gen',''}},
hu='$date1 első felében',
hr='prva polovina $date1',
it='prima metà del $date1',
ja='$date1上半期',
mk='прва половина на $date1',
ml='$date1 ആദ്യപകുതി',
nb='første halvdel av $date1',
nn='fyrste halvdelen av $date1',
nds='1. Helft $date1',
nl='eerste helft $date1',
no='første halvdel av $date1',
pl={'pierwsza połowa $date1', case={'gen',''}},
pt='primeira metade de $date1',
qu='$date1 ñawpaq kuskanpi',
ro='prima jumătate a lui $date1',
ru={'первая половина $date1', case={'gen',''}},
sk={'1. polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'prva polovica ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första halvan av $date1',
uk={'перша половина $date1', case={'gen',''}},
ur='نصف اول $date1',
vi='nửa đầu năm $date1',
['zh-hans']='$date1上半期',
['zh-hant']='$date1上半期',
default='<span style="color:red">first half of $date1</span>',
},
['secondhalf'] = {
nDates = 1,
ar='النصف الثاني من $date1',
['be-tarask']={'другая палова $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয়ার্ধ',
ca='2a meitat de $date1',
ckb='نیوەی دووەمی $date1',
cs={'2. polovina $date1', case={'gen',''}},
da='2. halvdel af $date1',
de='2. Hälfte $date1',
el='δεύτερο μισό του $date1',
en='second half of $date1',
['en-gb']='second half of $date1',
['en-ca']='second half of $date1',
eo='dua duono de $date1',
es='segunda mitad de $date1',
et='$date1 teine pool',
fa='نیمهٔ دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppupuolisko' end, case={'gen',''}},
fr='2<sup>e</sup> moitié de $date1',
gl='segunda metade de $date1',
he='במחצית השנייה של $date1',
hsb={'druha połojca $date1', case={'gen',''}},
hr='druga polovina $date1',
hu='$date1 második felében',
it='seconda metà del $date1',
ja='$date1下半期',
mk='втора половина на $date1',
ml='$date1 രണ്ടാം പകുതി',
nb='andre halvdel av $date1',
nn='andre halvdelen av $date1',
nds='2. Helft vun $date1',
nl='tweede helft $date1',
no='andre halvdel av $date1',
pl={'druga połowa $date1', case={'gen',''}},
pt='segunda metade de $date1',
qu='$date1 qhipaq kuskanpi',
ro='a doua jumătate a lui $date1',
ru={'вторая половина $date1', case={'gen',''}},
sk={'2. polovica $date1', case={'gen',''}},
sl={'druga polovica {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sv='andra halvan av $date1',
uk={'друга половина $date1', case={'gen',''}},
ur='نصف آخر $date1',
vi='nửa cuối năm $date1',
['zh-hans']='$date1下半期',
['zh-hant']='$date1下半期',
default='<span style="color:red">second half of $date1</span>',
},
['1quarter']= {
nDates = 1,
bn='$date1-এর প্রথম ত্রৈমাসিক',
['be-tarask']={'першая чвэрць $date1', case={'gen',''}},
ckb='چارەکی یەکەمی $date1',
cs={'1. čtvrtina $date1', case={'gen',''}},
de='erstes Viertel $date1',
en='first quarter of $date1',
['en-gb']='first quarter of $date1',
['en-ca']='first quarter of $date1',
eo='unua kvarono de $date1',
es='primer cuarto del $date1',
fa='ربع نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 ensimmäinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'premier quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון ראשון של $date1',
hsb={'prěnja štwórćina $date1', case={'gen',''}},
hr='prva četvrtina $date1',
it='primo quarto del $date1',
ja='$date1第1四半期',
ml='$date1 ഒന്നാം ത്രൈമാസം',
nb='første kvartal av $date1',
nl='eerste kwart $date1',
nn='fyrste kvartalet av $date1',
no='første kvartal av $date1',
pl={'pierwsza ćwiartka $date1', case={'gen',''}},
qu='$date1 ñawpaq ñiqin tawa phatmanpi',
ru={'первая четверть $date1', case={'gen',''}},
sk={'1. štvrtina $date1', case={'gen',''}},
sl={'prva četrtina {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sl={function(date1, state) return 'prva četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första kvartalet av $date1',
uk={'1-ша чверть $date1', case={'gen',''}},
ur='پہلی سہ ماہی $date1',
vi='quý 1 năm $date1',
['zh-hans']='$date1第一季度',
['zh-hant']='$date1第一季度',
default='<span style="color:red">first quarter of $date1</span>',
},
['2quarter']= {
nDates = 1,
['be-tarask']={'другая чвэрць $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয় ত্রৈমাসিক',
ckb='چارەکی دووەمی $date1',
cs={'2. čtvrtina $date1', case={'gen',''}},
de='zweites Viertel $date1',
en='second quarter of $date1',
['en-gb']='second quarter of $date1',
['en-ca']='second quarter of $date1',
eo='dua kvarono de $date1',
es='segundo cuarto del $date1',
fa='ربع دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 toinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'deuxième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שני של $date1',
hsb={'druha štwórćina $date1', case={'gen',''}},
hr='druga četvrtina $date1',
it='secondo quarto del $date1',
ja='$date1第2四半期',
ml='$date1 രണ്ടാം ത്രൈമാസം',
nb='andre kvartal av $date1',
nl='tweede kwart $date1',
nn='andre kvartalet av $date1',
no='andre kvartal av $date1',
pl={'druga ćwiartka $date1', case={'gen',''}},
qu='$date1 iskay ñiqin tawa phatmanpi',
ru={'вторая четверть $date1', case={'gen',''}},
sk={'2. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'druga četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='andra kvartalet av $date1',
uk={'2-га чверть $date1', case={'gen',''}},
ur='دوسری سہ ماہی $date1',
vi='quý 2 năm $date1',
['zh-hans']='$date1第二季度',
['zh-hant']='$date1第二季度',
default='<span style="color:red">second quarter of $date1</span>',
},
['3quarter']= {
nDates = 1,
['be-tarask']={'трэцяя чвэрць $date1', case={'gen',''}},
bn='$date1-এর তৃতীয় ত্রৈমাসিক',
ckb='چارەکی سێیەمی $date1',
cs={'3. čtvrtina $date1', case={'gen',''}},
de='drittes Viertel $date1',
en='third quarter of $date1',
['en-gb']='third quarter of $date1',
['en-ca']='third quarter of $date1',
eo='tria kvarono de $date1',
es='tercer cuarto del $date1',
fa='ربع سوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 kolmas neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'troisième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שלישי של $date1',
hsb={'třeća štwórćina $date1', case={'gen',''}},
hr='treća četvrtina $date1',
it='terzo quarto del $date1',
ja='$date1第3四半期',
ml='$date1 മൂന്നാം ത്രൈമാസം',
nb='tredje kvartal av $date1',
nl='derde kwart $date1',
nn='tredje kvartalet av $date1',
no='tredje kvartal av $date1',
pl={'trzecia ćwiartka $date1', case={'gen',''}},
qu='$date1 kimsa ñiqin tawa phatmanpi',
sk={'3. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'tretja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
ru={'третья четверть $date1', case={'gen',''}},
sv='tredje kvartalet av $date1',
uk={'3-тя чверть $date1', case={'gen',''}},
ur='تیسری سہ ماہی $date1',
vi='quý 3 năm $date1',
['zh-hans']='$date1第三季度',
['zh-hant']='$date1第三季度',
default='<span style="color:red">third quarter of $date1</span>',
},
['4quarter']= {
nDates = 1,
['be-tarask']={'чацьвёртая чвэрць $date1', case={'gen',''}},
bn='$date1-এর চতুর্থ ত্রৈমাসিক',
ckb='چارەکی چوارەمی $date1',
cs={'4. čtvrtina $date1', case={'gen',''}},
de='letztes Viertel $date1',
en='fourth quarter of $date1',
['en-gb']='fourth quarter of $date1',
['en-ca']='fourth quarter of $date1',
eo='kvara kvarono de $date1',
es='último cuarto del $date1',
fa='ربع چهارم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 viimeinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'quatrième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון רביעי של $date1',
hsb={'poslednja štwórćina $date1', case={'gen',''}},
hr='četvrta četvrtina $date1',
it='quarto quarto del $date1',
ja='$date1第4四半期',
ml='$date1 നാലാം ത്രൈമാസം',
nb='fjerde kvartal av $date1',
nl='vierde kwart $date1',
nn='tredje kvartalet av $date1',
no='fjerde kvartal av $date1',
pl={'czwarta ćwiartka $date1', case={'gen',''}},
qu='$date1 qhipaq ñiqin tawa phatmanpi',
ru={'последняя четверть $date1', case={'gen',''}},
sk={'4. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'zadnja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='fjärde kvartalet av $date1',
uk={'4-та чверть $date1', case={'gen',''}},
ur='چوتھی سہ ماہی $date1',
vi='quý 4 năm $date1',
['zh-hans']='$date1第四季度',
['zh-hant']='$date1第四季度',
default='<span style="color:red">forth quarter of $date1</span>',
},
-- ==========================================
-- === Seasons ==============================
-- ==========================================
['spring'] = {
nDates = 1,
ar='ربيع $date1',
['be-tarask']='вясна $date1',
bn='বসন্তকাল $date1',
ca='primavera $date1',
ckb='بەھاری $date1',
cs={'jaro $date1', case={'gen', ''}},
da='Forår $date1',
de='Frühling $date1',
en='Spring $date1',
['en-gb']='Spring $date1',
['en-ca']='Spring $date1',
eo='printempo $date1',
es='primavera de $date1',
et='$date1 kevad',
fa='بهار $date1',
fi='Kevät $date1',
fr='printemps $date1',
gl='primavera $date1',
he='אביב $date1',
hr='proljeće $date1',
hsb='nalěćo $date1',
hu='$date1 tavasza',
it='primavera $date1',
ja='{{#if:$date1|$date1年}}春',
mk='пролет $date1',
ml='$date1 വസന്തം',
nb='våren $date1',
nds='Vörjohr $date1',
nn='våren $date1',
nl='voorjaar $date1',
no='våren $date1',
pl='wiosna $date1',
pt='primavera de $date1',
ro='primăvara lui $date1',
ru='весна $date1',
sk={'jar $date1', case={'gen', ''}},
sl='pomlad $date1',
sv='våren $date1',
uk='весна $date1',
ur='بہار $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}春季',
['zh-hant']='{{#if:$date1|$date1年}}春季',
default='<span style="color:red">Spring $date1</span>',
},
['summer'] = {
nDates = 1,
ar='صيف $date1',
['be-tarask']='лета $date1',
bn='গ্রীষ্মকাল $date1',
ca='estiu $date1',
ckb='ھاوینی $date1',
cs={'léto $date1', case={'gen', ''}},
da='Sommer $date1',
de='Sommer $date1',
en='Summer $date1',
['en-gb']='Summer $date1',
['en-ca']='Summer $date1',
eo='somero $date1',
es='verano de $date1',
et='$date1 suvi',
fa='تابستان $date1',
fi='Kesä $date1',
fr='été $date1',
gl='verán $date1',
he='קיץ $date1',
hr='ljeto $date1',
hsb='lěćo $date1',
hu='$date1 nyara',
it='estate $date1',
ja='{{#if:$date1|$date1年}}夏',
mk='лето $date1',
ml='$date1 വേനൽ',
nb='sommeren $date1',
nds='Sommer $date1',
nn='sumaren $date1',
nl='zomer $date1',
no='sommeren $date1',
pl='lato $date1',
pt='verão de $date1',
ro='vara lui $date1',
ru='лето $date1',
sk={'leto $date1', case={'gen', ''}},
sl='poletje $date1',
sv='sommaren $date1',
uk='літо $date1',
ur='گرما $date1',
vi='mùa hè năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}夏季',
['zh-hant']='{{#if:$date1|$date1年}}夏季',
default='<span style="color:red">Summer $date1</span>',
},
['autumn'] = {
nDates = 1,
ar='خريف $date1',
['be-tarask']='восень $date1',
bn='শরত্কাল $date1',
ca='tardor $date1',
ckb='پاییزی $date1',
cs={'podzim $date1', case={'gen', ''}},
da='Efterår $date1',
de='Herbst $date1',
en='Autumn $date1',
['en-gb']='Autumn $date1',
['en-ca']='Autumn $date1',
eo='aŭtuno $date1',
es='otoño de $date1',
et='$date1 sügis',
fa='پاییز $date1',
fi='Syksy $date1',
fr='automne $date1',
gl='outono $date1',
he='סתיו $date1',
hr='jesen $date1',
hsb='nazyma $date1',
hu='$date1 ősze',
it='autunno $date1',
ja='{{#if:$date1|$date1年}}秋',
mk='есен $date1',
ml='$date1 ശരത്കാലം',
nb='høsten $date1',
nds='Harvst $date1',
nn='hausten $date1',
nl='herfst $date1',
no='høsten $date1',
pl='jesień $date1',
pt='outono de $date1',
ro='toamna lui $date1',
ru='осень $date1',
sk={'jeseň $date1', case={'gen', ''}},
sl='jesen $date1',
sv='hösten $date1',
uk='осінь $date1',
ur='خزاں $date1',
vi='mùa thu năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}秋季',
['zh-hant']='{{#if:$date1|$date1年}}秋季',
default='<span style="color:red">Autumn $date1</span>',
},
['winter'] = {
nDates = 1,
ar='شتاء $date1',
['be-tarask']='зіма $date1',
bn='শীতকাল $date1',
ca='hivern $date1',
ckb='زستانی $date1',
cs={'zima $date1', case={'gen', ''}},
da='Vinter $date1',
de='Winter $date1',
en='Winter $date1',
['en-gb']='Winter $date1',
['en-ca']='Winter $date1',
eo='vintro $date1',
es='invierno de $date1',
et='$date1 talv',
fa='زمستان $date1',
fi='Talvi $date1',
fr='hiver $date1',
gl='inverno $date1',
he='חורף $date1',
hr='zima $date1',
hsb='zyma $date1',
hu='$date1 tele',
it='inverno $date1',
ja='{{#if:$date1|$date1年}}冬',
mk='зима $date1',
ml='$date1 ശൈത്യകാലം',
nds='Winter $date1',
nl='winter $date1',
nb='vinteren $date1',
nn='vinteren $date1',
no='vinteren $date1',
pl='zima $date1',
pt='inverno de $date1',
ro='iarna lui $date1',
ru='зима $date1',
sk={'zima $date1', case={'gen', ''}},
sl='zima $date1',
sv='vintern $date1',
uk='зима $date1',
ur='سرما $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}冬季',
['zh-hant']='{{#if:$date1|$date1年}}冬季',
default='<span style="color:red">Winter $date1</span>',
},
-- ==========================================
-- === Decade/Century/Millennium ============
-- ==========================================
['decade'] = {
nDates = 1,
ar='أعوام $date1',
['be-tarask']='$date1-я гады',
bn='$date1-এর দশক',
ca='dècada del $date1',
ckb='دەیەی $date1',
cs=function(date1, state) return ((date1 % 100 == 0) and 'první dekáda' or ((date1 % 100) .. '. léta')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. století' end,
da="$date1'erne",
de='$date1er-Jahre',
el='δεκαετία του $date1',
en='$date1s',
['en-gb']='$date1s',
['en-ca']='$date1s',
eo='$date1-aj jaroj',
es='años $date1',
et='$date1ndad',
fa='دههٔ $date1',
fi='$date1-luvulla',
fr='années $date1',
gl='anos $date1',
he='שנות ה-$date1',
hr='$date1-ih',
hsb='$date1te lěta',
hu='$date1-s évek',
it="{{#ifexpr: $date1 mod 100 = 0 | primo decennio del {{Roman|{{#expr: ($date1 / 100) + 1}}}} sec. | anni '{{#expr: $date1 mod 100 }} del {{Roman|{{#expr: floor($date1 / 100) + 1}}}} sec.}}",
ja='$date1年代',
mk='$date1-ти',
ml='ദശകം $date1-ൽ',
nb='$date1-årene',
nn='$date1-åra',
nds='Johren $date1',
nl='jaren $date1',
no='$date1-årene',
pl=function(date1, state)
local str = 'lata '..(date1 % 100)..'. '
if (date1 % 100 == 0) then
str = 'pierwsza dekada '
elseif (date1 % 100 == 10) then
str = 'druga dekada '
end
return str .. Roman(math.floor(date1 / 100) + 1) .. ' wieku'
end,
pt='década de $date1',
qu='$date1 watakuna',
ro='anii $date1',
ru='$date1-е годы',
sk=function(date1, state) return ((date1 % 100 == 0) and 'prvé desaťročie' or ((date1 % 100) .. '. roky')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. storočia' end,
sl='$date1-ta leta',
sv='$date1-talet{{#switch:$date1|1300|1400|1500|1600|1700|1800|1900|2000|2100= (decennium)}}',
uk='$date1-ті роки',
ur='دہائی $date1',
vi='thập niên $date1',
['zh-hans']='$date1年代',
['zh-hant']='$date1年代',
default='<span style="color:red">$date1s</span>',
},
['century'] = {
nDates = 1,
ar='القرن $date1',
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' стагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' শতাব্দী' end,
ca='segle {{small-caps|{{lc:{{Roman year|$date1}}}}}}',
ckb='سەدەی $date1',
cs='$date1. století',
da='$date1. århundrede',
de='$date1. Jahrhundert',
el='$date1ος αιώνας',
en=function(date1, state) return Ordinal(date1, 'en') .. ' century' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' century' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' century' end,
eo='$date1-a jarcento',
es=function(date1, state) return 'siglo ' .. Roman(date1) end,
et='$date1. sajand',
fa='سدهٔ $date1',
fi='{{#expr: ($date1 - 1) * 100}}-luvulla',
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle' end,
gl=function(date1, state) return 'século ' .. Roman(date1) end,
he='המאה ה-$date1',
hr='$date1. stoljeće',
hsb='$date1. lětstotk',
hu='$date1. évszázad',
it=function(date1, state) return Roman(date1) .. ' sec.' end,
ja='$date1世紀',
la='saeculum $date1',
mk=function(date1, state) return Roman(date1) .. ' век' end,
ml='$date1-ആം നൂറ്റാണ്ട്',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' århundre' end,
nn=function(date1, state) return 'det ' .. Ordinal(date1, 'no') .. ' århundre' end,
nds='$date1. Johrhunnert',
nl='$date1e eeuw',
no=function(date1, state) return Ordinal(date1, 'no') .. ' århundre' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decCentury('pl',state.case) end,
pt=function(date1, state) return 'século ' .. Roman(date1) end,
qu='$date1 ñiqin pachakwata',
ro='secolul al $date1-lea',
ru=function(date1, state) return Roman(date1) .. ' век' end,
sk='$date1. storočie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. stoletje' end,
sv='{{#expr:$date1 - 1}}00-talet',
tt=function(date1, state) return Roman(date1) .. ' гасыр' end,
uk=function(date1, state) return Roman(date1) .. ' ст.' end,
ur='صدی $date1',
vi='thế kỷ $date1',
['zh-hans']='$date1世纪',
['zh-hant']='$date1世紀',
default='<span style="color:red">$date1 century</span>',
},
['millennium'] = {
nDates = 1,
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' тысячагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' সহস্রাব্দ' end,
ckb=function(date1, state) return 'ھەزارەی ' .. Ordinal(date1, 'ckb') end,
cs='$date1. tisíciletí',
de='$date1. Jahrtausend',
en=function(date1, state) return Ordinal(date1, 'en') .. ' millennium' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' millennium' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' millennium' end,
eo='$date1-a jarmilo',
es=function(date1, state) return Roman(date1) .. ' milenio' end,
fa=function(date1, state) return 'هزارهٔ ' .. Ordinal(date1, 'fa') end,
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' millénaire' end,
hr='$date1. tisućljeće',
hsb='$date1. lěttysac',
it=function(date1, state) return Roman(date1) .. ' millennio' end,
ja='$date1千年紀',
la='millennium $date1',
ml='$date1-മത്തെ സഹസ്രാബ്ദം',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' årtusen' end,
nn=function(date1, state) return Ordinal(date1, 'no') .. ' tusenår' end,
nl='$date1e millennium',
no=function(date1, state) return Ordinal(date1, 'no') .. ' årtusen' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decMillennium('pl',state.case) end,
pt=function(date1, state) return Roman(date1) .. ' milénio' end,
qu='$date1 ñiqin waranqawata',
ru=function(date1, state) return Roman(date1) .. ' тысячелетие' end,
sk='$date1. tisícročie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. tisočletje' end,
sv=function(date1, state) return Ordinal(date1, 'sv') .. ' årtusendet' end,
uk=function(date1, state) return Roman(date1) .. ' тис.' end,
ur=function(date1, state) return 'ہزارہ ' .. Ordinal(date1, 'ur') end,
vi='thiên niên kỷ $date1',
['zh-hans']=function(date1, state) return Ordinal(date1, 'zh-hans') .. '千年' end,
['zh-hant']=function(date1, state) return Ordinal(date1, 'zh-hant') .. '千年' end,
--default='{{Please translate|$date1 millennium|Module:I18n/complex date}}',
default='<span style="color:red">$date1 millennium</span>',
},
['million'] = {
ckb = '$date1 میلیۆن ساڵ',
de = '$date1 Millionen Jahre',
en = '$date1 million years',
['en-gb'] = '$date1 million years',
['en-ca'] = '$date1 million years',
eo = '$date1 milionoj da jaroj',
es = '$date1 millones de años',
nl = '$date1e miljoen jaar',
fr = '$date1 millions d’années',
it = '$date1 milioni di anni',
ml = function(date1, state)
if date1 >= 10 then
return (date1 / 10) .. ' കോടി വർഷങ്ങൾ' -- 100 lakh = 1 crore
else
return (date1 * 10) .. ' ലക്ഷം വർഷങ്ങൾ'
end
end,
nb = '$date1 millioner år',
nl = '$date1 miljoen jaar',
nn = '$date1 millionar år',
pl = '$date1 milionów lat',
pt = '$date1 milhões de anos',
ru = '$date1 миллиона лет',
uk = '$date1 мільйони років',
ur = '$date1 ملین سال',
['zh-hans'] = '$date1百万年',
['zh-hant'] = '$date1百万年',
default='<span style="color:red">$date1 million years</span>',
nDates = 1,
},
['billion'] = {
ckb = '$date1 بیلیۆن ساڵ',
de = '$date1 Milliarden Jahre',
en = '$date1 billion years',
['en-gb'] = '$date1 billion years',
['en-ca'] = '$date1 billion years',
es = '$date1 mil millones de años',
nl = '$date1e miljard jaar',
fr = '$date1 milliard d’années',
it = '$date1 miliardi di anni',
ml = function(date1, state) return (date1 * 10) .. ' കോടി വർഷങ്ങൾ' end,
nb = '$date1 milliarder år',
nl = '$date1 miljard jaar',
nn = '$date1 milliardar år',
pl = '$date1 miliardów lat',
pt = '$date1 mil milhões de anos',
ru = '$date1 миллиардов лет',
uk = '$date1 мільярдів років',
ur = '$date1 بلین سال',
['zh-hans'] = '$date1十亿年',
['zh-hant'] = '$date1十億年',
default='<span style="color:red$date1 billion years</span>',
nDates = 1,
},
-- ==========================================
-- === Complex phrases ======================
-- ==========================================
-- Phrases which for some languages can be build out of simpler phrases
-- Their "default" state, used when no translation in the requested language was found, returns a "state" like
-- table which is used to call the function again and built the phrase out of simple phrases
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the century'] = {
nDates = 2,
['be-tarask']=function(date1, date2, state) return 'канец ' .. Roman(date1) ..' — пачатак ' .. Roman(date2) ..' стагодзьдзя' end,
bn=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'bn') .. ' শতাব্দীর পালাবদলে' end,
ckb=function(date1, date2, state) return 'سەردەمی $date1/' .. Ordinal(date2, 'ckb') .. ' سەدە' end,
cs='přelom $date1. a $date2. století',
de=function(date1, date2, state) return 'Jahrhundertwende $date1/' .. Ordinal(date2, 'de') end,
en=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en') .. ' century' end,
['en-gb']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-gb') .. ' century' end,
['en-ca']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-ca') .. ' century' end,
eo='ŝanĝo de la $date1-a al la $date2-a jarcentoj',
es=function(date1, date2, state) return 'hacia finales del siglo ' .. Roman(date1) .. ' y principios del siglo ' .. Roman(date2) .. '' end,
fi='{{#expr: $date1 * 100-100}}-luvun loppu ja {{#expr: $date2 * 100-100}}-luvun alku',
fr=function(date1, date2, state) return 'fin du ' .. Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle / début du ' .. Ordinal(date2, 'fr', 'roman', 'm') end,
hr='kraj $date1. i početak $date2. stoljeća',
hsb=function(date1, date2, state) return 'přechod ' .. Roman(date1) ..' do ' .. Roman(date2) ..' lětstotka' end,
it=function(date1, date2, state) return 'fine del ' .. Roman(date1) .. ' sec. e inizio del ' .. Roman(date2) .. ' sec.' end,
ja='$date1世紀から$date2世紀への変わり目',
ml='$date1 നൂറ്റാണ്ട് അവസാനവും $date2 നൂറ്റാണ്ട് ആദ്യവും',
nb=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nb') .. ' århundre/begynnelsen av det ' .. Ordinal(date2, 'nb') .. ' århundre' end,
nl=function(date1, date2, state) return 'eind $date1e/begin $date2e eeuw' end,
nn=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nn') .. ' hundreåret/byrjinga av det ' .. Ordinal(date2, 'nn') .. ' hundreåret' end,
pl=function(date1, date2, state) return 'przełom ' .. Roman(date1) ..' i ' .. Roman(date2) ..' wieku' end,
qu="$date1 ñiqin waranqawatamanta $date2 ñiqin waranqawataman t'ikrasqanpi",
ru=function(date1, date2, state) return 'конец ' .. Roman(date1) ..' — начало ' .. Roman(date2) ..' века' end,
sk='prelom $date1. a $date2. storočia',
sl=function(date1, date2, state) return 'pozno ' .. Ordinal(date1, 'sl') .. '. in zgodnje ' .. Ordinal(date2, 'sl') .. '. stoletje' end,
sv=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'sv') .. ' årtusendets sekelskifte' end,
uk=function(date1, date2, state) return 'кін. ' .. Roman(date1) ..' ст. — поч. ' .. Roman(date2) ..' ст.' end,
vi='bước sang thế kỷ $date2',
['zh-hans']=function(date1, date2, state) return '$date1世纪末' .. Ordinal(date2, 'zh-hans') .. '世纪初' end,
['zh-hant']=function(date1, date2, state) return '$date1世紀末' .. Ordinal(date2, 'zh-hant') .. '世紀初' end,
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='century', era1=state.era1, adj2 = 'early', unit2='century', era2=state.era2} end,
},
-- $date1 - original date - 10
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the decade']={
nDates = 2,
['be-tarask']='канец $date1-х — пачатак $date2-х гг.',
bn='$date1-এর দশকের শেষে এবং $date2-এর দশকের শুরুতে',
cs=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "přelom 1. a 2. dekády " .. century1 .. ". století"
elseif decade2 == 0 and date2 < date1 then
return "přelom 9. a 10. dekády " .. century1 .. ". století"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "přelom " .. century1 .. ". a " .. century2 .. ". století"
else
return "přelom " .. decade1 .. "0. a " .. decade2 .. "0. let " .. century1 .. ". století"
end
end,
de='späte $date1er und frühe $date2er',
ckb='کۆتایییەکانی $date1 و سەرەتاکانی $date2',
en='late $date1s and early $date2s',
['en-gb']='late $date1s and early $date2s',
['en-ca']='late $date1s and early $date2s',
eo='ŝanĝo de la $date1-a al la $date2-a jardekoj',
es='finales de los años $date1 y principios de los años $date2',
fa='اواخر دههٔ $date1 و اوایل دههٔ $date2',
fi='$date1-luvun loppu ja $date2-luvun alku',
fr='fin des années $date1 / début des années $date2',
hr='kraj $date1-ih i početak $date2-ih',
hsb='pózdnje $date1te a zažne $date2te lěta',
it='fine degli anni $date1 / inizio degli anni $date2',
ja='$date1年代末から$date2年代初頭',
ml='$date1കൾ അവസാനവും $date2കൾ ആദ്യവും',
nb = 'sent på $date1-tallet og tidlig på $date2-tallet',
nl = 'eind $date1e/begin $date2e decennium',
nn = 'seint på $date1-talet og tidleg på $date2-talet',
pl=function(date1, date2, state)
local decade1 = math.floor(date1 % 100)
local decade2 = math.floor(date2 % 100)
local century1 = Roman(math.floor( date1 / 100) + 1)
local century2 = Roman(math.floor( date2 / 100) + 1)
if decade1 == 0 and date1 < date2 then
return "przełom pierwszej i drugiej dekady " .. century1 .. " wieku"
elseif decade1 == 10 and date1 < date2 then
return "przełom drugiej dekady i lat 20. " .. century1 .. " wieku"
elseif decade1 == 90 and decade2 == 0 then
return "przełom ostatniej dekady " .. century1 .. " i pierwszej dekady " .. century2 .. " wieku"
else
return "przełom lat " .. decade1 .. ". i " .. decade2 .. ". " .. century1 .. " wieku"
end
end,
qu='qhipaq $date1 watakuna ñawpaq $date2 watakunapas',
ru='конец $date1-х — начало $date2-х гг.',
sv='sena $date1-talet och tidiga $date2-talet',
sk=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "prelom 1. a 2. desaťročia " .. century1 .. ". storočia"
elseif decade2 == 0 and date2 < date1 then
return "prelom 9. a 10. desaťročia " .. century1 .. ". storočia"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "prelom " .. century1 .. ". a " .. century2 .. ". storočia"
else
return "prelom " .. decade1 .. "0. a " .. decade2 .. "0. rokov " .. century1 .. ". storočia"
end
end,
sl='pozna $date1-ta in zgodnja $date2-ta leta',
uk='кінець $date1-х — початок $date2-х рр.',
ur='اخیر دہائی $date1 و ابتدا دہائی $date2',
vi='bước sang thập niên $date2',
['zh-hans']='$date1年代末$date2年代初',
['zh-hant']='$date1年代末$date2年代初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='decade', era1=state.era1, adj2 = 'early', unit2='decade', era2=state.era2} end,
},
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the year']={
nDates = 2,
['be-tarask']='канец $date1 — пачатак $date2 г.',
bn='$date1-এর শেষে এবং $date2-এর শুরুতে',
ckb='کۆتاییی $date1 و سەرەتای $date2',
cs='přelom roku $date1/$date2',
de='Jahreswechsel $date1/$date2',
en='late $date1 and early $date2',
['en-gb']='late $date1 and early $date2',
['en-ca']='late $date1 and early $date2',
eo='jarŝanĝo $date1 / $date2',
es='finales de $date1 y principios de $date2',
fa='اواخر $date1 و اوایل $date2',
fi='vuoden $date1 loppu ja vuoden $date2 alku',
fr='fin $date1 / début $date2',
hsb='kónc $date1/spočatk $date2',
ja='$date1年末から$date2年初め',
ml='$date1 അവസാനവും $date2 ആദ്യവും',
nb='årsskiftet $date1/$date2',
nl='eind $date1 en begin $date2',
nn='årsskiftet $date1/$date2',
pl='koniec $date1 i początek $date2 roku',
qu="$date1 qhipaq p'unchawkuna $date2 ñawpaq p'unchawkunapas",
ru='конец $date1 — начало $date2 г.',
sk='prelom rokov $date1/$date2',
sl='konec leta $date1 in začetek $date2',
sv='sena $date1 och tidiga $date2',
uk='кінець $date1 — початок $date2 р.',
ur='اخیر $date1 و ابتدا $date2',
vi='bước sang năm $date2',
['zh-hans']='$date1年末$date2年初',
['zh-hant']='$date1年末$date2年初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='year', era1=state.era1, adj2 = 'early', unit2='year', era2=state.era2} end,
},
['circa2'] = {
nDates = 2,
ar='حوالي $date1 – $date2',
['be-tarask']={'прыкладна з $date1 да прыкладна $date2', case={'gen','gen'}},
bn='প্রায় $date1 এবং প্রায় $date2-এর মাঝামাঝি',
ca='cap a $date1 o $date2',
ckb='بە نزیکی لەنێوان $date1 و $date2',
cs='cca $date1 až $date2',
da='ca. mellem $date1 og $date2',
de='etwa $date1 bis $date2',
el='περίπου μεταξύ $date1 και $date2',
en='between circa $date1 and circa $date2',
['en-gb']='between circa $date1 and circa $date2',
['en-ca']='between circa $date1 and circa $date2',
eo='proksimume de $date1 ĝis $date2',
es='hacia $date1 o $date2',
et='ca. $date1 ja $date2 vahel',
fa='حدوداً بین $date1 و $date2',
fi={function(date1, date2, state) return 'suunnilleen ' .. decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='vers $date1-$date2',
gl='entre $date1 e $date2',
he='בסביבות ה- $date1 עד $date2',
hr='oko između $date1 i $date2',
hsb={'wot něhdźe $date1 do něhdźe $date2', case={'gen','gen'}},
hu='$date1 és $date2 között',
it='tra il $date1 e il $date2 circa',
ja='$date1頃と$date2頃の間',
mk='~ помеѓу $date1 и $date2',
ml='ഉദ്ദേശം $date1-നും ഉദ്ദേശം $date2-നും ഇടയ്ക്ക്',
nb='ca. mellom $date1 og $date2',
nn='ca. mellom $date1 og $date2',
nds='üm un bi $date1 bet $date2',
nl='ca. $date1 tot $date2',
no='ca. mellom $date1 og $date2',
pl={'od około $date1 do około $date2', case={'gen','gen'}},
pt='cerca de $date1 a $date2',
qu="$date1 p'unchawmantachá $date2 p'unchawkamachá",
ro='circa $date1 – $date2',
ru={'с около $date1 до, примерно, $date2', case={'gen','gen'}},
sk='cca $date1 až $date2',
sl={function(date1, date2, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1 do $date2' end, case={'gen','gen'}},
sv='c:a $date1 till $date2',
uk={'з близько $date1 до, приблизно, $date2', case={'gen','gen'}},
ur='قریباً بین $date1 و $date2',
vi='giữa vào khoảng $date1 và vào khoảng $date2',
['zh-hans']='约$date1~$date2间',
['zh-hant']='約$date1~$date2間',
default=function(date1, date2, state) return {conj='between', adj1 = 'circa', unit1=state.unit1, era1=state.era1, adj2 = 'circa', unit2=state.unit2, era2=state.era2} end,
},
-- ==========================================
-- === Eras and calendars ===================
-- ==========================================
['julian'] = {
nDates = 2,
ar='$date1 ($date2 حسب [[w:ar:التقويم اليولياني|تقويم يولياني]])',
['be-tarask']='$date1 ($date2 паводле [[w:be-tarask:Юліянскі каляндар|ст. ст.]]',
bn='$date1 (জুলিয়ান ক্যালেন্ডারে $date2)',
ca='$date1 ($date2 segons el [[w:ca:Calendari julià|calendari julià]])',
ckb='$date1 ($date2 بەپێی [[w:ڕۆژژمێری جولیانی|ڕۆژژمێری جولیانی]])',
cs='$date1 ($date2 podle [[w:cs:Juliánský kalendář|juliánského kalendáře]])',
da='$date1 ($date2 [[w:da:Julianske kalender|Julianske kalender]])',
de='$date1 ($date2 [[w:de:Julianischer Kalender|Julianischer Kalender]])',
el='$date1 ($date2 με το [[w:el:Ιουλιανό ημερολόγιο|Ιουλιανό ημερολόγιο]])',
en='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-gb']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-ca']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
fa='$date1 ($date2 در [[w:fa:Old Style and New Style dates|تقویم جولیان]])',
eo='$date1 ($date2 laŭ la [[w:eo:Julia kalendaro|Julia Kalendaro]])',
es='$date1 ($date2 según el [[w:es:Calendario juliano|calendario juliano]])',
et='$date1 ($date2 [[w:et:Juliuse kalender|Juliuse kalendris]])',
fi='$date1 ($date2 [[w:fi:juliaaninen kalenteri|juliaanisen kalenterin]] mukaan)',
fr='$date1 ($date2 dans le [[w:fr:calendrier julien|calendrier julien]])',
gl='$date1 ($date2 segundo o [[w:gl:Calendario xuliano|calendario xuliano]])',
hu='$date1 ($date2 a [[w:hu:Julianus-naptár|Julián naptárban]])',
hr='$date1 ($date2 prema [[w:hr:Julijanski kalendar|julijanskom kalendaru]])',
he='$date1 ($date2 ב[[w:he:לוח היוליאני|הלוח היוליאני]])',
hsb='$date1 ($date2 po julianiskej protyce)',
it='$date1 ($date2 secondo il [[w:it:Calendario giuliano|calendario giuliano]])',
ja='$date1([[w:ja:ユリウス暦|ユリウス暦]] $date2)',
mk='$date1 ($date2 по [[w:mk:Јулијански календар|Јулијанскиот календар]])',
ml='$date1 ([[w:ml:ജൂലിയൻ കാലഗണനാരീതി|ജൂലിയൻ വർഷത്തിൽ]] $date2)',
ml='$date1 ([[w:Old Style and New Style dates|ജൂലിയൻ കാലഗണനാരീതിയിൽ]] $date2)',
nds='$date1 ($date2 na’n [[w:nds:Juliaansch Klenner|Juliaanschen Klenner]])',
nb='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nn='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nl='$date1 ($date2 volgens de [[w:nl:Juliaanse kalender|Juliaanse kalender]])',
no='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalender]])',
pl='$date1 ($date2 wedle [[w:pl:Daty nowego i starego porządku|kalendarza juliańskiego]])',
pt='$date1 ($date2 no [[w:pt:Mudança para o calendário gregoriano|calendário juliano]])',
qu='$date1 ($date2 [[w:qu:Hulyanu kalindaryu|Hulyanu kalindaryupi]])',
ro='$date1 ($date2 în [[w:ro:calendarul iulian|calendarul iulian]])',
ru='$date1 ($date2 [[w:ru:Юлианский календарь|по ст. ст.]])',
sk='$date1 ($date2 podľa [[w:sk:Juliánsky kalendár|juliánskeho kalendára]])',
sl='$date1 ($date2 po [[w:sl:Julijanski koledar|julijanskem koledarju]])',
sv='$date1 ($date2 [[w:sv:Gamla stilen|gamla stilen]])',
uk='$date1 ($date2 [[w:uk:Юліанський календар|Юліанський календар]])',
ur='$date1 ($date2 حسب [[w:ur:نئے اور پانے انداز کی تاریخ|جولین تقویم]])',
vi='$date1 ($date2 trong [[w:vi:Lịch Julius|lịch Juliêng]])',
['zh-hans']='$date1([[w:Old Style and New Style dates|格里历]]$date2)',
['zh-hant']='$date1([[w:Old Style and New Style dates|格里曆]]$date2)',
default='<span style="color:red">$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])</span>',
},
['ad'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:ад Нараджэньня Хрыста|пасьля Н. Х.]]',
bn ='$date1 খ্রিস্টাব্দ',
ca ='$date1 dC',
ckb ='$date1 [[w:ckb:ئانۆ دۆمینی|پاز]]',
cs ='$date1 n. l.',
de ='$date1 n. Chr.',
el ='$date1 μ.Χ.',
en ='$date1 [[w:en:Anno Domini|AD]]',
['en-gb'] ='$date1 [[w:en:Anno Domini|AD]]',
['en-ca'] ='$date1 [[w:en:Anno Domini|AD]]',
eo ='$date1 pK',
es ='$date1 [[w:es:Anno Dómini|d. C.]]',
fa ='$date1 م.',
fi ='$date1 jaa.',
fr ='$date1 ap. J.-C.',
gl ='$date1 d.C.',
he ='$date1 לספירה',
hu ='i. sz. $date1',
hr ='$date1 p. Kr.',
hsb='$date1 po Chr.',
it ='$date1 d.C.',
ja ='西暦$date1',
mk ='$date1 н.е.',
ml ='ക്രി.വ. $date1',
nb ='$date1 e.Kr.',
nl ='$date1 n.Chr.',
nn ='$date1 e.Kr.',
no ='$date1 e.Kr.',
pl ='$date1 [[w:pl:naszej ery|n.e.]]',
ro ='$date1 d.Hr.',
ru ='$date1 н. э.',
sk ='$date1 po Kr.',
sl ='$date1 n. št.',
sv ='$date1 [[w:sv:Efter Kristus|e.Kr.]]',
uk ='$date1 н. е.',
ur ='$date1 ء',
vi ='$date1 [[w:vi:Công Nguyên|CN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元]]$date1',
default='<span style="color:red">$date1 [[w:en:Anno Domini|AD]]</span>',
},
['ah'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:Ісламскі каляндар|хіджры]]',
bn ='$date1 [[w:bn:ইসলামি বর্ষপঞ্জি|হিজরী]]',
ckb ='$date1 [[w:ckb:ساڵی کۆچی|سک]]',
cs ='$date1 [[w:cs:Anno Hegirae|AH]]',
en ='$date1 [[w:en:Hijri year|AH]]',
['en-gb'] ='$date1 [[w:en:Hijri year|AH]]',
['en-ca'] ='$date1 [[w:en:Hijri year|AH]]',
eo ='$date1 post la [[w:eo:Heĝiro|Heĝiro]]',
fa ='$date1 ه.ق.',
fr ='$date1 [[w:fr:Calendrier musulman|A.H.]]',
he ="$date1 [[w:he:הלוח המוסלמי|להג'רה]]",
hr ='$date1 god. hidžre',
ja ='[[w:ja:ヒジュラ暦|ヒジュラ暦]]$date1',
ml ='$date1 [[w:ml:ഇസ്ലാമിക കലണ്ടർ|ഹിജ്റ]]',
nb ='$date1 [[w:no:Den muslimske kalenderen|A.H.]]',
nn ='$date1 [[w:nn:Den muslimske kalenderen|A.H.]]',
pl ='$date1 [[w:pl:Kalendarz muzułmański|A.H.]]',
ru ='$date1 [[w:ru:Исламский календарь|хиджры]]',
sk ='$date1 [[w:sk:A. H.|A. H.]]',
sv ='$date1 [[w:sv:Muslimska kalendern|A.H.]]',
uk ='$date1 [[w:uk:Мусульманський календар|хіджри]]',
ur ='$date1 ھ',
vi ='$date1 [[w:vi:Lịch Hồi giáo|AH]]',
['zh-hans'] ='[[w:en:Hijri year|Hijri历]]$date1',
['zh-hant'] ='[[w:en:Hijri year|Hijri曆]]$date1',
default='<span style="color:red">$date1 [[w:en:Hijri year|AH]]</span>',
},
['bc'] = {
nDates = 1,
ar ='$date1 ق.م.',
['be-tarask']='$date1 [[w:be-tarask:да Нараджэньня Хрыста|да Н. Х.]]',
bn ='খ্রিস্টপূর্ব $date1',
ca ='$date1 [[w:ca:Abans de Crist|aC]]',
ckb ='$date1 [[w:ckb:پێش زایین|پز]]',
cs ='$date1 [[w:cs:Př. n. l.|př. n. l.]]',
de ='$date1 [[w:de:v. Chr.|v. Chr.]]',
el ='$date1 π.Χ.',
en ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-gb'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-ca'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
eo ='$date1 aK',
es ='$date1 [[w:es:Antes de Cristo|a. C.]]',
fa ='$date1 پ.م.',
fi ='$date1 eaa.',
fr ='$date1 av. J.-C.',
gl ='$date1 [[w:gl:Antes de Cristo|a.C.]]',
he ='$date1 לפני הספירה',
hr ='$date1 [[w:hr:Pr. Kr.|pr. Kr.]]',
hsb='$date1 do Chr.',
hu ='i. e. $date1',
it ='$date1 [[w:it:Avanti Cristo|a.C.]]',
ja ='[[w:ja:紀元前|紀元前]]$date1',
mk ='$date1 п.н.е.',
ml ='ക്രി.മു. $date1',
nds='$date1 vör uns Tied',
nb ='$date1 f.Kr.',
nl ='$date1 v.Chr.',
nn ='$date1 f.Kr.',
no ='$date1 f.Kr.',
pl ='$date1 [[w:pl:przed naszą erą|p.n.e.]]',
ro ='$date1 î.Hr.',
ru ='$date1 до н. э.',
sk ='$date1 [[w:sk:Pred Kristom|pred Kr.]]',
sl ='$date1 pr. n. št.',
sv ='$date1 [[w:sv:Före Kristus|f.Kr.]]',
uk ='$date1 до н. е.',
ur ='$date1 ق م',
vi ='$date1 [[w:vi:Trước Công Nguyên|TCN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元前]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元前]]$date1',
default='<span style="color:red">$date1 [[w:en:Ante Christum Natum|BC]]</span>',
},
['bp'] = {
nDates = 1,
ar ='$date1 [[w:ar:قبل الحاضر|قبل الحاضر]]',
be ='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
['be-tarask']='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
bn ='বর্তমানের আগে $date1',
ca ='$date1 [[w:ca:BP (unitat de temps)|BP]]',
ckb ='$date1 [[w:ckb:پێش ئێستا|پێش ئێستا]]',
cs ='$date1 [[w:cs:B. P.|B. P.]]',
de ='$date1 [[w:de:Before Present|BP]]',
el ='$date1 [[w:en:Before Present|BP]]',
en ='$date1 [[w:en:Before Present|BP]]',
['en-gb'] ='$date1 [[w:en:Before Present|BP]]',
['en-ca'] ='$date1 [[w:en:Before Present|BP]]',
eo='$date1 [[w:eo:Before Present|BP]]',
es ='$date1 [[w:es:Before Present|BP]]',
et ='$date1 [[w:et:Before Present|BP]]',
fa ='$date1 پیش از اکنون',
fi ='$date1 [[w:fi:BP (ajanlasku)|BP]]',
fr ='$date1 [[w:fr:Avant le présent|avant le présent]]',
hr ='$date1 [[w:hr:BP|prije današnjice]]',
it ='$date1 [[w:it:Before Present|BP]]',
ja ='$date1 [[w:ja:BP (年代測定)|BP]]',
lv ='$date1 [[w:lv:Pirms mūsdienām|Pirms mūsdienām]]',
mk ='$date1 [[w:mk:Пред сегашноста|п.с.]]',
ml ='[[w:en:Before Present|ഇന്നത്തെ കാലത്തിനു മുമ്പ്]] $date1',
nb ='$date1 [[w:no:Before Present|BP]]',
nn ='$date1 [[w:nn:Before Present|BP]]',
nl ='$date1 [[w:nl:Before Present|BP]]',
no ='$date1 [[w:no:Before Present|BP]]',
pl ='$date1 [[w:pl:Before Present|BP]]',
pt ='$date1 [[w:pt:Antes do Presente|Antes do Presente]]',
ru ='$date1 [[w:ru:До настоящего времени|до н. в.]]',
sk ='$date1 [[w:sk:BP (čas)|BP]]',
sl ='$date1 [[w:sl:pred sedanjostjo|p. s.]]',
sr ='$date1 [[w:sr:Прије садашњости|Прије садашњости]]',
sv ='$date1 [[w:sv:B.P.|B.P.]]',
uk ='$date1 [[w:uk:Років тому|Років тому]]',
ur ='$date1 قبل حاضر',
vi ='$date1 [[w:en:Before Present|trước ngày nay]]',
['zh-hans'] ='[[w:zh:距今|距今]]$date1',
['zh-hant'] ='[[w:zh:距今|距今]]$date1',
default='<span style="color:red">$date1 [[w:en:Before Present|BP]]</span>',
},
}
-- List of possible string inputs to "adj", "junction", "era", and "precision" fields
-- Each term is matched to its synonym and the role: a - adjective, e - era
-- p - precision, c - calendar and combos, r - certainty and j - conjunction
p.Synonyms = {
-- adjectives and prepositions
['quarter1']={'1quarter','a'},
['1stquarter']={'1quarter','a'},
['1st quarter']={'1quarter','a'},
['first quarter']={'1quarter','a'},
['1quarter']={'1quarter','a'},
['quarter2']={'2quarter','a'},
['2ndquarter']={'2quarter','a'},
['2nd quarter']={'2quarter','a'},
['second quarter']={'2quarter','a'},
['2quarter']={'2quarter','a'},
['quarter3']={'3quarter','a'},
['3rdquarter']={'3quarter','a'},
['3rd quarter']={'3quarter','a'},
['third quarter']={'3quarter','a'},
['3quarter']={'3quarter','a'},
['quarter4']={'4quarter','a'},
['4thquarter']={'4quarter','a'},
['4th quarter']={'4quarter','a'},
['fourth quarter']={'4quarter','a'},
['4quarter']={'4quarter','a'},
['spring']={'spring','a'},
['summer']={'summer','a'},
['fall']={'autumn','a'},
['autumn']={'autumn','a'},
['winter']={'winter','a'},
['beginning']={'early','a'},
['early']={'early','a'},
['1half']={'firsthalf','a'},
['1sthalf']={'firsthalf','a'},
['1st half']={'firsthalf','a'},
['firsthalf']={'firsthalf','a'},
['first half']={'firsthalf','a'},
['end']={'late','a'},
['late']={'late','a'},
['middle']={'mid','a'},
['mid']={'mid','a'},
['2half']={'secondhalf','a'},
['2ndhalf']={'secondhalf','a'},
['2nd half']={'secondhalf','a'},
['secondhalf']={'secondhalf','a'},
['second half']={'secondhalf','a'},
['<']={'before','a'},
['b']={'before','a'},
['before']={'before','a'},
['>']={'after','a'},
['a']={'after','a'},
['after']={'after','a'},
['~']={'circa','a'},
['c']={'circa','a'},
['ca']={'circa','a'},
['circa']={'circa','a'},
['by']={'by','a'},
['from']={'from','a'},
['until']={'until','a'},
['uncertain']={'uncertain','r'},
['possibly']={'possibly','r'},
['probably']={'probably','r'},
['presumably']={'presumably','r'},
-- eras
['bc']={'bc','e'},
['bce']={'bc','e'},
['before present']={'bp','e'},
['bp']={'bp','e'},
['ce']={'ad','e'},
['ad']={'ad','e'},
['ah']={'ah','e'},
-- conjunctions
['&']={'and','j'},
['and']={'and','j'},
['or']={'or','j'},
['-']={'from-until','j'},
['–']={'from-until','j'},
['from-until']={'from-until','j'},
['between']={'between','j'},
['circa2']={'circa2','a'},
-- precision
['day']={'day','p'},
['month']={'month','p'},
['year']={'year','p'},
['s']={'decade','p'},
['decade']={'decade','p'},
['century']={'century','p'},
['millennium']={'millennium','p'},
['million']={'million','p'},
['billion']={'billion','p'},
-- calendar
['julian']={'julian','c'},
['islamic']={'islamic','c'},
['hijri']={'islamic','c'},
-- "Turn of the ..." options are a combos which will be handled in similar way as calendars
['turn of the century']={'turn of the century','c'},
['century turn']={'turn of the century','c'},
['turn of the decade']={'turn of the decade','c'},
['decade turn']={'turn of the decade','c'},
['turn of the year']={'turn of the year','c'},
['year turn']={'turn of the year','c'},
-- other dates which will be handled in similar way as calendars
['?']={'unknown','c'},
['unknown']={'unknown','c'},
['unknown year']={'year unknown','c'},
['year unknown']={'year unknown','c'},
}
return p
dewwfi4vqnuvm2x2yyc13jj2an5fuuv
803611
803610
2020-11-24T21:55:02Z
en>Pppery
0
Use c. instead of circa per [[Template talk:Wikidata date]]
803611
Scribunto
text/plain
--[[
__ __ _ _ ___ _ ___ __ _ _ _
| \/ | ___ __| |_ _| | ___ _|_ _/ |( _ ) _ __ / /__ ___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_)| || |/ _ \| '_ \ / / __/ _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_ | || | (_) | | | |/ / (_| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)___|_|\___/|_| |_/_/ \___\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This code was copied from Commons please request changes there.
Maintainers:
*Jarekt
]]
local function Ordinal(...)
return require('Module:Ordinal-cd')._Ordinal(...)
end
local function Roman(...)
return require('Module:Roman-cd')._Numeral(...)
end
-- ==========================================================================
-- === Local Helper functions ==============================================
-- ==========================================================================
-- form of French "of" based on "precision", which is wikibase compatible date precision format:
-- 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- 1 2 3 4 5 mill cent deca year month day
local frOf = {'du','du','du','du','du', 'du', 'du', 'de', 'de', 'de', 'du'}
local function decYear(state,lang,case)
--Declension of word "year" in several languages
if state.precision == 9 then
yearCase = {
fi={xxx='vuosien', gen='vuoden', ill='vuoteen', ptv='vuotta', ela='vuodesta', ine='vuonna'},
sl={insP='letoma', ins='letom', gen='leta', loc='letu'},
hsb={insP='lětami', ins='lětom', gen='lěta', loc='lěće'}
}
return yearCase[lang][case] .. ' '
else
return ''
end
end
local function decCentury(lang,case)
--Declension of word "century" in several languages
centuryCase = {
pl={nom='wiek', gen='wieku', ins='wiekiem', loc='wieku', default = 'wiek'},
hsb={nom='lětstotk', gen='lětstotka', ins='lětstotkom', loc='lětstotku', default = 'lětstotk'}
}
return centuryCase[lang][case] or centuryCase[lang]['default']
end
local function decMillennium(lang,case)
--Declension of word "century" in several languages
millenniumCase = {
pl={nom='tysiąclecie', gen='tysiąclecia', ins='tysiącleciem', loc='tysiącleciu', default = 'tysiąclecie'},
hsb={nom='lěttysac', gen='lěttysaca', ins='lěttysacom', loc='lěttysacu', default = 'lěttysac'}
}
return millenniumCase[lang][case] or millenniumCase[lang]['default']
end
-- ==========================================================================
-- === Output data structure ===============================================
-- ==========================================================================
local p = {}
--[[
Multiple formats are possible:
1) string with key words "$date1" and "$date2" which will be replaced later by the date strings
2) function which can be used to create output string
* Function INPUTS:
- date1 and date2 string with date strings. "$date1" and "$date2" can also be used which will be replaced latter
- state is a table with full description of the phrase we are trying to construct. It includes state.precision1 (and 2)
which is wikibase compatible date precision format: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
3) table with following fields:
* table[1] is either string or a function
* table.case is a 2 element table with Grammatical cases used by given language to alter
month name or other adjective related to date # 1 and 2
]]
p.Translations = {
-- ==========================================
-- === Two date phrases =====================
-- ==========================================
['from-until'] = {
nDates = 2,
ar='من $date1 إلى $date2',
['be-tarask']={'з $date1 да $date2', case={'gen','gen'}},
bn='$date1 থেকে $date2 পর্যন্ত',
ca='$date1 fins $date2',
ckb='لە $date1 تاوەکوو $date2',
cs='$date1 až $date2',
da='fra $date1 til $date2',
de='$date1 bis $date2',
el='από $date1 έως $date2',
en='from $date1 until $date2',
['en-gb']='from $date1 until $date2',
['en-ca']='from $date1 until $date2',
eo='de $date1 al $date2',
es='de $date1 a $date2',
et='$date1 kuni $date2',
fa='از $date1 تا $date2',
fi='$date1–$date2', -- välisenä aikana
fr=function(date1, date2, state) -- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision > 10 then -- precision > month
return 'du $date1 au $date2'
else -- "DE septebmbre" / "D'octobre"
return require("Module:Linguistic").of(date1, 'fr') .. " à $date2"
end
end,
gl='de $date1 a $date2', --Template:From until used 'desde $date1 ata $date2'
he='מ- $date1 עד $date2',
hr='od $date1 do $date2',
hsb={'wot $date1 do $date2', case={'gen','gen'}},
hu='$date1 – $date2',
it='dal $date1 al $date2',
ja='$date1から$date2まで',
mk='од $date1 до $date2',
ml='$date1 മുതൽ $date2 വരെ',
nb='fra $date1 til $date2',
nds='$date1 bet $date2',
nn='fra $date1 til $date2',
nl='$date1 tot $date2',
no='fra $date1 til $date2',
pl={'od $date1 do $date2', case={'gen','gen'}},
ro='din $date1 până în $date2',
pt='de $date1 a $date2',
qu="$date1 p'unchawmanta $date2 p\'unchawkama",
ru={'с $date1 по $date2', case={'gen','gen'}},
sk={'od $date1 do $date2', case={'gen','gen'}},
sl={'od $date1 do $date2', case={'gen','gen'}},
sv='från $date1 till $date2',
uk={'з $date1 до $date2', case={'gen','gen'}},
ur='از $date1 تا $date2',
vi='từ $date1 đến $date2',
['zh-hans']='从$date1到$date2',
['zh-hant']='從$date1到$date2',
default='<span style="color:red">$date1 - $date2</span>',
},
['between']={
nDates = 2,
ar='بين $date1 و $date2',
['be-tarask']={'між $date1 і $date2', case={'ins','ins'}},
bn='$date1 এবং $date2-এর মাঝামাঝি',
ca='entre $date1 i $date2',
ckb='لەنێوان $date1 و $date2',
cs={'mezi $date1 a $date2', case={'ins','ins'}},
da='mellem $date1 og $date2',
de='zwischen $date1 und $date2',
el='μεταξύ $date1 και $date2',
en='between $date1 and $date2',
['en-gb']='between $date1 and $date2',
['en-ca']='between $date1 and $date2',
eo='inter $date1 kaj $date2',
es='entre $date1 y $date2',
et='$date1 ja $date2 vahel',
fa='بین $date1 و $date2',
fi={function(date1, date2, state) return decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='entre $date1 et $date2',
gl='entre $date1 e $date2',
he='בין $date1 ו- $date2',
hr='između $date1 i $date2',
hsb={'mjez $date1 a $date2', case={'ins','ins'}},
hu='$date1 és $date2 között',
hi='$date1 और $date2 के बीच',
it='tra il $date1 e il $date2',
ja='$date1と$date2の間',
mk='помеѓу $date1 и $date2',
ml='$date1, $date2 എന്നിവയ്ക്കിടയ്ക്ക്',
nb='mellom $date1 og $date2',
nds='twischen $date1 un $date2',
nn='mellom $date1 og $date2',
nl='tussen $date1 en $date2',
no='mellom $date1 og $date2',
pl={'między $date1 a $date2', case={'ins','ins'}},
ro='între $date1 și $date2',
pt='entre $date1 e $date2',
qu="$date1 p'unchawpa $date2 p'unchawpapas chawpin",
ru={'между $date1 и $date2', case={'ins','ins'}},
sk={'medzi $date1 a $date2', case={'ins','ins'}},
sl={function(date1, date2, state) return 'med ' .. decYear(state,'sl','insP') .. '$date1 in $date2' end, case={'ins','ins'}},
sv='mellan $date1 och $date2',
uk={'між $date1 та $date2', case={'ins','ins'}},
ur='بین $date1 و $date2',
vec='tra el $date1 e el $date2',
vi='giữa $date1 và $date2',
['zh-hans']='在$date1到$date2之间',
['zh-hant']='在$date1到$date2之間',
default='<span style="color:red">$date1 - $date2</span>',
},
['or'] = {
nDates = 2,
ar='$date1 أو $date2',
['be-tarask']='$date1 ці $date2',
bn='$date1 অথবা $date2',
ca='$date1 o $date2',
ckb='$date1 یان $date2',
cs='$date1 nebo $date2',
da='$date1 eller $date2',
de='$date1 oder $date2',
el='$date1 ή $date2',
en='$date1 or $date2',
['en-gb']='$date1 or $date2',
['en-ca']='$date1 or $date2',
eo='$date1 aŭ $date2',
es='$date1 o $date2',
et='$date1 või $date2',
fa='$date1 یا $date2',
fi='$date1 tai $date2',
fr='$date1 ou $date2',
gl='$date1 ou $date2',
he='$date1 או $date2',
hr='$date1 ili $date2',
hsb='$date1 abo $date2',
hu='$date1 vagy $date2',
it='$date1 o $date2',
ja='$date1または$date2',
mk='$date1 или $date2',
ml='$date1 അഥവാ $date2',
nb='$date1 eller $date2',
nn='$date1 eller $date2',
nds='$date1 oder $date2',
nl='$date1 of $date2',
no='$date1 eller $date2',
pl='$date1 lub $date2',
pt='$date1 ou $date2',
qu='$date1 icha $date2',
ro='$date1 sau $date2',
ru='$date1 или $date2',
sk='$date1 alebo $date2',
sl='$date1 ali $date2',
sv='$date1 eller $date2',
uk='$date1 або $date2',
ur='$date1 یا $date2',
th='$date1 หรือ $date2',
vi='$date1 hoặc $date2',
['zh-hans']='$date1或$date2',
['zh-hant']='$date1或$date2',
default=function(date1, date2, state) return require("Module:linguistic").conj({date1, date2}, state.lang, 'or') end,
},
['and'] = {
nDates = 2,
ar='$date1 و $date2',
['be-tarask']='$date1 і $date2',
bn='$date1 এবং $date2',
ca='$date1 i $date2',
ckb='$date1 و $date2',
cs='$date1 a $date2',
da='$date1 og $date2',
de='$date1 und $date2',
el='$date1 και $date2',
en='$date1 and $date2',
['en-gb']='$date1 and $date2',
['en-ca']='$date1 and $date2',
eo='$date1 kaj $date2',
es='$date1 y $date2',
et='$date1 ja $date2',
fa='$date1 و $date2',
fi='$date1 ja $date2',
fr='$date1 et $date2',
gl='$date1 e $date2',
he='$date1 ו- $date2',
hu='$date1 és $date2',
hsb='$date1 a $date2',
it='$date1 e $date2',
ja='$date1および$date2',
mk='$date1 и $date2',
ml='$date1 ഒപ്പം $date2',
nb='$date1 og $date2',
nn='$date1 og $date2',
nds='$date1 un $date2',
nl='$date1 en $date2',
no='$date1 og $date2',
pl='$date1 i $date2',
pt='$date1 e $date2',
qu='$date1wan $date2pas',
ro='$date1 și $date2',
ru='$date1 и $date2',
sk='$date1 a $date2',
sl='$date1 in $date2',
sv='$date1 och $date2',
uk='$date1 та $date2',
ur='$date1 و $date2',
vi='$date1 và $date2',
['zh-hans']='$date1和$date2',
['zh-hant']='$date1和$date2',
default=function(date1, date2, state) return require("Module:linguistic").conj({date1, date2}, state.lang, 'and') end,
},
-- ==========================================
-- === Prepositions =========================
-- ==========================================
['by'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'да $date1', case={'gen',''}},
bn='$date1 কর্তৃক',
ca='vers $date1',
ckb='نزیکی $date1',
cs={'kolem $date1', case={'gen',''}},
da='mod $date1',
de='spätestens $date1',
el='έως $date1',
en='by $date1',
['en-gb']='by $date1',
['en-ca']='by $date1',
eo='ne post $date1',
es='para $date1',
et='hiljemalt $date1',
fa='توسط $date1',
fi={function(date1, state) return decYear(state,'fi','ill') .. '$date1 mennessä' end, case={'ill',''}},
fr='$date1 (au plus tard)',
gl='cara a $date1',
he='עד $date1',
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='entro il $date1',
ja='$date1まで',
mk='кон $date1',
ml='$date1-ൽ',
nb='innen $date1',
nn='innan $date1',
nds='$date1 oder vördem al',
nl='tegen $date1',
no='innen $date1',
pl={'przed $date1', case={'ins',''}},
pt='por $date1',
qu="$date1 p'unchawkama",
ro='de $date1',
ru={'к $date1', case={'dat',''}},
sk={'okolo $date1', case={'gen',''}},
sl={function(date1, state) return 'najpozneje ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='senast $date1',
uk={'до $date1', case={'dat',''}},
ur='توسط $date1',
vi='vào $date1', -- need to insert "đã" somewhere later in the sentence -->
['zh-hans']='$date1前后',
['zh-hant']='$date1前后',
default='<span style="color:red">by $date1</span>',
},
['before'] = {
nDates = 1,
ar='قبل $date1',
['be-tarask']={'перад $date1', case={'ins',''}},
bn='$date1-এর আগে',
bg='Преди $date1',
ca='abans $date1',
ckb='پێش $date1',
cs={'před $date1', case={'ins',''}},
da='før $date1',
de='vor $date1',
el='πριν από $date1',
en='before $date1',
['en-gb']='before $date1',
['en-ca']='before $date1',
eo='antaŭ $date1',
es='antes de $date1',
et='enne $date1',
fa='پیش از $date1',
fi={function(date1, state) return 'ennen ' .. decYear(state,'fi','ptv') .. '$date1' end, case={'ptv',''}},
fr='avant $date1',
gl='antes do $date1',
he='לפני $date1',
hr='prije $date1',
hsb={'do $date1', case={'gen',''}},
hu='$date1 előtt',
it='prima del $date1',
ja='$date1より前',
mk='пред $date1',
ml='$date1-നു മുമ്പ്',
nb='før $date1',
nn='før $date1',
nds='vör $date1',
nl='voor $date1',
no='før $date1',
pl={'przed $date1', case={'ins',''}},
pt='antes de $date1',
qu="$date1 p'unchaw ñawpan",
ro='înainte de $date1',
ru={'ранее $date1', case={'gen',''}},
sk={'pred $date1', case={'ins',''}},
sl={function(date1, state) return 'pred ' .. decYear(state,'sl','ins') .. ' $date1' end, case={'ins',''}},
sv='före $date1',
uk={'до $date1', case={'gen',''}},
ur='قبل $date1',
vi='trước $date1',
['zh-hans']='在$date1之前',
['zh-hant']='在$date1之前',
default='<span style="color:red">before $date1</span>',
},
['from'] = {
nDates = 1,
ar='من $date1',
['be-tarask']={'з $date1', case={'gen',''}},
bn='$date1 থেকে',
ckb='لە $date1',
cs={'od $date1', case={'gen',''}},
de='Seit $date1',
el='από $date1',
en='from $date1',
['en-gb']='from $date1',
['en-ca']='from $date1',
eo='ekde $date1',
es='desde $date1',
et='alates $date1',
fa='از $date1',
fi={function(date1, state) return decYear(state,'fi','ela') .. '$date1 alkaen' end, case={'ela',''}},
fr= function(date1, state) return 'à partir '.. frOf[state.precision] ..' $date1' end,
gl='desde o $date1',
he='מ- $date1',
hr='od $date1',
hsb={'wot $date1', case={'gen',''}},
it='dal $date1',
ja='$date1から',
nds='vonaf $date1',
mk='од $date1',
nb='fra $date1',
nn='frå $date1',
ml='$date1 മുതൽ',
nl='vanaf $date1',
no='fra $date1',
pl={'od $date1', case={'gen',''}},
qu="$date1 p'unchawmanta",
ro='din $date1',
ru={'С $date1', case={'gen',''}},
sk={'od $date1', case={'gen',''}},
sl={function(date1, state) return 'od ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='från $date1',
uk={'з $date1', case={'gen',''}},
ur='از $date1',
vi='từ $date1',
['zh-hans']='自$date1',
['zh-hant']='自$date1',
default='<span style="color:red">from $date1</span>',
},
['until'] = {
nDates = 1,
['be-tarask']={'усутыч да $date1', case={'gen',''}},
bn='$date1 পর্যন্ত',
ckb='تاوەکوو $date1',
cs={'do $date1', case={'gen', ''}},
de='bis $date1',
en='until $date1',
['en-gb']='until $date1',
['en-ca']='until $date1',
eo='ĝis $date1',
es='hasta $date1',
fr=function(date1, state)
if state.precision > 10 then -- precision > month
return "jusqu'au $date1"
else
return "jusqu'à $date1"
end
end,
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='al $date1',
nb='til $date1',
nl='tot $date1',
nn='til $date1',
ml='$date1 വരെ',
pl={'do $date1', case={'gen',''}},
ru={'вплоть до $date1', case={'gen',''}},
sk={'do $date1', case={'gen', ''}},
uk={'аж до $date1', case={'gen',''}},
ur='تک $date1',
['zh-hans']='直到$date1',
['zh-hant']='直到$date1',
default='<span style="color:red">until $date1</span>',
},
['after'] = {
nDates = 1,
ar='بعد $date1',
['be-tarask']={'пасьля $date1', case={'gen',''}},
bn='$date1-এর পরে',
ca='després de $date1',
ckb='دوای $date1',
cs={'po $date1', case={'loc',''}},
de='nach $date1',
da='efter $date1',
el='μετά από $date1',
en='after $date1',
['en-gb']='after $date1',
['en-ca']='after $date1',
eo='post $date1',
es='después de $date1',
et='pärast $date1',
fa='پس از $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 jälkeen' end, case={'gen',''}},
fr='après $date1',
gl='despois do $date1',
he='לאחר $date1',
hr='nakon $date1',
hu='$date1 után',
hsb={'po $date1', case={'loc',''}},
it='dopo il $date1',
ja='$date1より後',
mk='по $date1',
ml='$date1-നു ശേഷം',
nb='etter $date1',
nn='etter $date1',
nds='na $date1',
nl='na $date1',
no='etter $date1',
pl={'po $date1', case={'loc',''}},
pt='após $date1',
qu="$date1 p'unchaw qhipan",
ro='după $date1',
ru={'после $date1', case={'gen',''}},
sk={'po $date1', case={'loc',''}},
sl={function(date1, state) return 'po ' .. decYear(state,'sl','loc') .. ' $date1' end, case={'loc',''}},
sv='efter $date1',
uk={'після $date1', case={'gen',''}},
ur='بعد $date1',
vi='sau $date1',
['zh-hans']='在$date1之后',
['zh-hant']='在$date1之後',
default='<span style="color:red">after $date1</span>',
},
-- ==========================================
-- === Adjectives ===========================
-- ==========================================
['early'] = {
nDates = 1,
ar='بداية $date1',
['be-tarask']={'пачатак $date1', case={'gen',''}},
bn='$date1-এর শুরুতে',
ca='al principi de $date1',
ckb='سەرەتای $date1',
cs={'začátek $date1', case={'gen',''}},
da='tidlig $date1',
de='Anfang $date1',
el='αρχές του $date1',
en='early $date1',
['en-gb']='early $date1',
['en-ca']='early $date1',
eo='komence de $date1',
es='a principios de $date1',
et='$date1 algus',
fa='اوایل $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alku' end, case={'gen',''}},
fr='début $date1',
gl='a principios de $date1',
he='תחילת $date1',
hr='početak $date1',
hsb={'spočatk $date1', case={'gen',''}},
hu='$date1 elején',
it='inizio del $date1',
ja='$date1初頭',
mk='почеток на $date1',
ml='$date1 ആദ്യപാദം',
nb='tidlig $date1',
nn='tidleg $date1',
nds='Anfang $date1',
nl='begin $date1',
no='tidlig $date1',
pl={'początek $date1', case={'gen',''}},
pt='a princípios de $date1',
qu="$date1 ñawpaq p'unchawkunapi",
ro='începând cu $date1',
ru={'начало $date1', case={'gen',''}},
sk={'začiatok $date1', case={'gen',''}},
sl=function(date1, state)-- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision == 9 then -- precision > month
return 'zgodnje leto $date1'
else -- "DE septebmbre" / "D'octobre"
return 'zgodnji $date1'
end
end,
sv='början av $date1',
uk={'початок $date1', case={'gen',''}},
ur='ابتدا $date1',
vi='đầu năm $date1',
['zh-hans']='$date1早期',
['zh-hant']='$date1早期',
default='<span style="color:red">early $date1</span>',
},
['mid'] = {
nDates = 1,
ar='منتصف $date1',
['be-tarask']={'сярэдзіна $date1', case={'gen',''}},
bn='$date1-এর মাঝামাঝি',
ca='a mitjan $date1',
ckb='ناوەڕاستی $date1',
cs={'polovina $date1', case={'gen',''}},
da='midten af $date1',
de='Mitte $date1',
el='μέσα του $date1',
en='mid $date1',
['en-gb']='mid $date1',
['en-ca']='mid $date1',
eo='meze de $date1',
es='a mediados de $date1',
et='$date1 keskel',
fa='اواسط $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 puoliväli' end, case={'gen',''}},
fr='mi $date1',
gl='a mediados de $date1',
he='אמצע $date1',
hr='sredina $date1',
hsb={'srjedź $date1', case={'gen',''}},
hu='$date1 közepén',
it='metà del $date1',
ja='$date1半ば',
mk='средина на $date1',
ml='$date1 മദ്ധ്യം',
nb='midten av $date1',
nn='midten av $date1',
nds='Midd $date1',
nl='midden $date1',
no='midten av $date1',
pl={'środek $date1', case={'gen',''}},
pt='meados de $date1',
qu="$date1 chawpin p'unchawkunapi",
ro='mijlocul lui $date1',
ru={'середина $date1', case={'gen',''}},
sk={'polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'sredina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='mitten av $date1',
uk={'середина $date1', case={'gen',''}},
ur='وسط $date1',
vi='giữa năm $date1',
['zh-hans']='$date1中期',
['zh-hant']='$date1中期',
default='<span style="color:red">mid $date1</span>',
},
['late'] = {
nDates = 1,
ar='نهاية $date1',
bn='$date1-এর শেষে',
['be-tarask']={'канец $date1', case={'gen',''}},
ca='al final de $date1',
ckb='کۆتاییی $date1',
cs='konec $date1',
da='sen $date1',
de='Ende $date1',
el='τέλη του $date1',
en='late $date1',
['en-gb']='late $date1',
['en-ca']='late $date1',
eo='fine de $date1',
es='a fines de $date1',
et='$date1 lõpp',
fa='اواخر $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppu' end, case={'gen',''}},
fr='fin $date1',
gl='a finais de $date1',
he='סוף $date1',
hr='kraj $date1',
hsb={'kónc $date1', case={'gen',''}},
hu='$date1 végén',
it='fine del $date1',
ja='$date1末',
mk='крајот на $date1',
ml='$date1 അന്ത്യപാദം',
nb='sent $date1',
no='seint $date1',
nds='Enn $date1',
nl='eind $date1',
no='sent $date1',
pl={'koniec $date1', case={'gen',''}},
pt='ao final de $date1',
qu="$date1 qhipaq p'unchawkunapi",
ro='sfârșitul lui $date1',
ru={'конец $date1', case={'gen',''}},
sk={'koniec $date1', case={'gen',''}},
sl={function(date1, state) return 'pozno ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='slutet av $date1',
uk={'кінець $date1', case={'gen',''}},
ur='اخیر $date1',
vi='cuối năm $date1',
['zh-hans']='$date1晚期',
['zh-hant']='$date1晚期',
default='<span style="color:red">late $date1</span>',
},
['circa'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'каля $date1', case={'gen',''}},
bn='প্রায় $date1',
ckb='بەنزیکی $date1',
ca='cap a $date1',
cs='cca $date1',
da='ca. $date1',
de='etwa $date1',
el='περ. $date1',
en='c. $date1',
['en-gb']='circa $date1',
['en-ca']='circa $date1',
eo='ĉirkaŭ $date1',
es='hacia $date1', -- suggestion use "hacia"/"hacia el" or leave the latin circa or ca.
et='ca. $date1',
fa='حوالی $date1', -- template:Circa used 'حدود $date1'
fi={function(date1, state) return 'suunnilleen ' .. decYear(state,'fi','ine') .. '$date1' end, case={'ine',''}}, -- template:Circa used 'noin $date1'
fr=function(date1, state)
if state.precision >= 11 then -- precision >= day
return 'vers le $date1'
else
return 'vers $date1'
end
end,
gl='entre $date1', -- template:Circa used 'cara a $date1'
he='בסביבות $date1',
hr='oko $date1',
hsb={'wokoło $date1', case={'gen',''}},
hu='$date1 körül',
it='$date1 circa',
ja='$date1頃',
ko='$date1경',
mk='~ $date1', -- template:Circa used 'o. $date1'
ml= 'ഉദ്ദേശം $date1',
nb='ca. $date1',
nn='ca. $date1',
nds='üm un bi $date1',
nl='ca. $date1',
no='ca. $date1',
pl={'około $date1', case={'gen',''}},
pt='cerca de $date1',
qu='$date1chá',
ro='circa $date1',
ru={'около $date1', case={'gen',''}},
sk='cca $date1',
sl={function(date1, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='c:a $date1', -- template:Circa used 'ca $date1'
uk={'близько $date1', case={'gen', ''}},
ur='قریباً $date1',
vi='vào khoảng $date1',
['zh-hans']='约$date1',
['zh-hant']='約$date1',
default='<span style="color:red">circa $date1</span>',
},
['uncertain'] = {
nDates = 1,
['zh-hans']='$date1(?)',
['zh-hant']='$date1(?)',
default='$date1 (?)',
},
probably = { -- see Q56644435
nDates = 1,
ast = 'probablemente $date1',
bn = 'সম্ভবত $date1',
ca = 'probablement $date1',
ckb = 'لەوانەیە $date1',
cs = 'pravděpodobně $date1',
de = 'wahrscheinlich $date1',
el = 'πιθανόν $date1',
en = 'probably $date1',
['en-gb']='probably $date1',
['en-ca']='probably $date1',
eo = 'probable $date1',
es = 'probable $date1',
fa = 'احتمالاً $date1',
fr = 'probablement $date1',
he = 'כפי הנראה $date1',
hu = 'feltehetőleg $date1',
it = 'probabile $date1',
mk = 'веројатно $date1',
ml = 'ഒരുപക്ഷേ $date1',
nl = 'waarschijnlijk $date1',
pl = 'prawdopodobnie $date1',
pt = 'provavelmente $date1',
ru = 'вероятно $date1',
sv = 'antagligen $date1',
uk = 'ймовірно $date1',
default='<span style="color:red">probably $date1</span>',
},
possibly = { -- see Q30230067
nDates = 1,
ast = 'posiblemente $date1',
bn = 'যথাসম্ভব $date1',
ckb = 'لەوانەیە $date1',
cs = 'možná $date1',
de = 'möglicherweise $date1',
en = 'possibly $date1',
['en-gb']='possibly $date1',
['en-ca']='possibly $date1',
eo = 'eble $date1',
el = 'πιθανόν $date1',
fa = 'احتمالاً $date1',
fr = 'peut-être $date1',
hu = 'talán $date1',
mk = 'можеби $date1',
ml = 'മിക്കവാറും $date1',
nl = 'mogelijk $date1',
pl = 'być może $date1',
pt = 'possivelmente $date1',
ro = 'poate $date1',
ru = 'возможно $date1',
sv = 'möjligen $date1',
uk = 'можливо $date1',
default='<span style="color:red">possibly $date1</span>',
},
presumably = { -- see Q18122778
nDates = 1,
ast = 'presumiblemente $date1',
ckb = 'دەشێت $date1',
de = 'vermutlich $date1',
en = 'presumably $date1',
['en-gb']='presumably $date1',
['en-ca']='presumably $date1',
fr = 'présumé $date1',
it = 'presumibilmente $date1',
ml = 'ഊഹപ്രകാരം $date1',
ru = 'предположительно $date1',
pl = 'przypuszczalnie $date1',
uk = 'здогадно $date1',
default='<span style="color:red">presumably $date1</span>',
},
['year unknown']= {
nDates = 0,
ca='Any desconegut',
['be-tarask']='Год невядомы',
bn='অজানা বছর',
ckb='ساڵی نەزاندراو',
cs='neznámý rok',
de='Jahr unbekannt',
el='άγνωστο έτος',
en='year unknown',
['en-gb']='year unknown',
['en-ca']='year unknown',
eo='jaro nesciata',
es='Año desconocido',
fa='سال نامعلوم',
fi='tuntematon vuosi',
fr='Année inconnue',
he='שנה לא ידועה',
hsb='lěto njeznate',
hr='nepoznata godina',
it='anno sconosciuto',
ja='年代不明',
la='Sine anno',
ml='വർഷം ലഭ്യമല്ല',
mk='непозната година',
nb='ukjent år',
nn='ukjent år',
nl='zonder jaar',
no='ukjent år',
pl='rok nieznany',
qu='Mana riqsisqa wata',
ru='Год неизвестен',
sk='neznámy rok',
sl='neznano leto',
sv='Okänt år',
uk='рік невідомий',
ur='سال نامعلوم',
vi='không rõ năm',
['zh-hans']='年份未知',
['zh-hant']='年份未知',
default='<span style="color:red">year unknown</span>',
},
-- ==========================================
-- === Parts of a whole =====================
-- ==========================================
['firsthalf'] = {
nDates = 1,
ar='النصف الأول من $date1',
['be-tarask']={'першая палова $date1', case={'gen',''}},
bn='$date1-এর প্রথমার্ধ',
ca='1a meitat de $date1',
ckb='نیوەی یەکەمی $date1',
cs={'1. polovina $date1', case={'gen',''}},
da='1. halvdel af $date1',
de='1. Hälfte $date1',
el='πρώτο μισό του $date1',
en='first half of $date1',
['en-gb']='first half of $date1',
['en-ca']='first half of $date1',
eo='unua duono de $date1',
es='primera mitad de $date1',
et='$date1 esimene pool',
fa='نیمهٔ نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alkupuolisko' end, case={'gen',''}},
fr='1<sup>re</sup> moitié de $date1',
gl='primeira metade de $date1',
he='במחצית הראשונה של $date1',
hsb={'prěnja połojca $date1', case={'gen',''}},
hu='$date1 első felében',
hr='prva polovina $date1',
it='prima metà del $date1',
ja='$date1上半期',
mk='прва половина на $date1',
ml='$date1 ആദ്യപകുതി',
nb='første halvdel av $date1',
nn='fyrste halvdelen av $date1',
nds='1. Helft $date1',
nl='eerste helft $date1',
no='første halvdel av $date1',
pl={'pierwsza połowa $date1', case={'gen',''}},
pt='primeira metade de $date1',
qu='$date1 ñawpaq kuskanpi',
ro='prima jumătate a lui $date1',
ru={'первая половина $date1', case={'gen',''}},
sk={'1. polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'prva polovica ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första halvan av $date1',
uk={'перша половина $date1', case={'gen',''}},
ur='نصف اول $date1',
vi='nửa đầu năm $date1',
['zh-hans']='$date1上半期',
['zh-hant']='$date1上半期',
default='<span style="color:red">first half of $date1</span>',
},
['secondhalf'] = {
nDates = 1,
ar='النصف الثاني من $date1',
['be-tarask']={'другая палова $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয়ার্ধ',
ca='2a meitat de $date1',
ckb='نیوەی دووەمی $date1',
cs={'2. polovina $date1', case={'gen',''}},
da='2. halvdel af $date1',
de='2. Hälfte $date1',
el='δεύτερο μισό του $date1',
en='second half of $date1',
['en-gb']='second half of $date1',
['en-ca']='second half of $date1',
eo='dua duono de $date1',
es='segunda mitad de $date1',
et='$date1 teine pool',
fa='نیمهٔ دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppupuolisko' end, case={'gen',''}},
fr='2<sup>e</sup> moitié de $date1',
gl='segunda metade de $date1',
he='במחצית השנייה של $date1',
hsb={'druha połojca $date1', case={'gen',''}},
hr='druga polovina $date1',
hu='$date1 második felében',
it='seconda metà del $date1',
ja='$date1下半期',
mk='втора половина на $date1',
ml='$date1 രണ്ടാം പകുതി',
nb='andre halvdel av $date1',
nn='andre halvdelen av $date1',
nds='2. Helft vun $date1',
nl='tweede helft $date1',
no='andre halvdel av $date1',
pl={'druga połowa $date1', case={'gen',''}},
pt='segunda metade de $date1',
qu='$date1 qhipaq kuskanpi',
ro='a doua jumătate a lui $date1',
ru={'вторая половина $date1', case={'gen',''}},
sk={'2. polovica $date1', case={'gen',''}},
sl={'druga polovica {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sv='andra halvan av $date1',
uk={'друга половина $date1', case={'gen',''}},
ur='نصف آخر $date1',
vi='nửa cuối năm $date1',
['zh-hans']='$date1下半期',
['zh-hant']='$date1下半期',
default='<span style="color:red">second half of $date1</span>',
},
['1quarter']= {
nDates = 1,
bn='$date1-এর প্রথম ত্রৈমাসিক',
['be-tarask']={'першая чвэрць $date1', case={'gen',''}},
ckb='چارەکی یەکەمی $date1',
cs={'1. čtvrtina $date1', case={'gen',''}},
de='erstes Viertel $date1',
en='first quarter of $date1',
['en-gb']='first quarter of $date1',
['en-ca']='first quarter of $date1',
eo='unua kvarono de $date1',
es='primer cuarto del $date1',
fa='ربع نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 ensimmäinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'premier quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון ראשון של $date1',
hsb={'prěnja štwórćina $date1', case={'gen',''}},
hr='prva četvrtina $date1',
it='primo quarto del $date1',
ja='$date1第1四半期',
ml='$date1 ഒന്നാം ത്രൈമാസം',
nb='første kvartal av $date1',
nl='eerste kwart $date1',
nn='fyrste kvartalet av $date1',
no='første kvartal av $date1',
pl={'pierwsza ćwiartka $date1', case={'gen',''}},
qu='$date1 ñawpaq ñiqin tawa phatmanpi',
ru={'первая четверть $date1', case={'gen',''}},
sk={'1. štvrtina $date1', case={'gen',''}},
sl={'prva četrtina {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sl={function(date1, state) return 'prva četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första kvartalet av $date1',
uk={'1-ша чверть $date1', case={'gen',''}},
ur='پہلی سہ ماہی $date1',
vi='quý 1 năm $date1',
['zh-hans']='$date1第一季度',
['zh-hant']='$date1第一季度',
default='<span style="color:red">first quarter of $date1</span>',
},
['2quarter']= {
nDates = 1,
['be-tarask']={'другая чвэрць $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয় ত্রৈমাসিক',
ckb='چارەکی دووەمی $date1',
cs={'2. čtvrtina $date1', case={'gen',''}},
de='zweites Viertel $date1',
en='second quarter of $date1',
['en-gb']='second quarter of $date1',
['en-ca']='second quarter of $date1',
eo='dua kvarono de $date1',
es='segundo cuarto del $date1',
fa='ربع دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 toinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'deuxième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שני של $date1',
hsb={'druha štwórćina $date1', case={'gen',''}},
hr='druga četvrtina $date1',
it='secondo quarto del $date1',
ja='$date1第2四半期',
ml='$date1 രണ്ടാം ത്രൈമാസം',
nb='andre kvartal av $date1',
nl='tweede kwart $date1',
nn='andre kvartalet av $date1',
no='andre kvartal av $date1',
pl={'druga ćwiartka $date1', case={'gen',''}},
qu='$date1 iskay ñiqin tawa phatmanpi',
ru={'вторая четверть $date1', case={'gen',''}},
sk={'2. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'druga četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='andra kvartalet av $date1',
uk={'2-га чверть $date1', case={'gen',''}},
ur='دوسری سہ ماہی $date1',
vi='quý 2 năm $date1',
['zh-hans']='$date1第二季度',
['zh-hant']='$date1第二季度',
default='<span style="color:red">second quarter of $date1</span>',
},
['3quarter']= {
nDates = 1,
['be-tarask']={'трэцяя чвэрць $date1', case={'gen',''}},
bn='$date1-এর তৃতীয় ত্রৈমাসিক',
ckb='چارەکی سێیەمی $date1',
cs={'3. čtvrtina $date1', case={'gen',''}},
de='drittes Viertel $date1',
en='third quarter of $date1',
['en-gb']='third quarter of $date1',
['en-ca']='third quarter of $date1',
eo='tria kvarono de $date1',
es='tercer cuarto del $date1',
fa='ربع سوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 kolmas neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'troisième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שלישי של $date1',
hsb={'třeća štwórćina $date1', case={'gen',''}},
hr='treća četvrtina $date1',
it='terzo quarto del $date1',
ja='$date1第3四半期',
ml='$date1 മൂന്നാം ത്രൈമാസം',
nb='tredje kvartal av $date1',
nl='derde kwart $date1',
nn='tredje kvartalet av $date1',
no='tredje kvartal av $date1',
pl={'trzecia ćwiartka $date1', case={'gen',''}},
qu='$date1 kimsa ñiqin tawa phatmanpi',
sk={'3. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'tretja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
ru={'третья четверть $date1', case={'gen',''}},
sv='tredje kvartalet av $date1',
uk={'3-тя чверть $date1', case={'gen',''}},
ur='تیسری سہ ماہی $date1',
vi='quý 3 năm $date1',
['zh-hans']='$date1第三季度',
['zh-hant']='$date1第三季度',
default='<span style="color:red">third quarter of $date1</span>',
},
['4quarter']= {
nDates = 1,
['be-tarask']={'чацьвёртая чвэрць $date1', case={'gen',''}},
bn='$date1-এর চতুর্থ ত্রৈমাসিক',
ckb='چارەکی چوارەمی $date1',
cs={'4. čtvrtina $date1', case={'gen',''}},
de='letztes Viertel $date1',
en='fourth quarter of $date1',
['en-gb']='fourth quarter of $date1',
['en-ca']='fourth quarter of $date1',
eo='kvara kvarono de $date1',
es='último cuarto del $date1',
fa='ربع چهارم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 viimeinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'quatrième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון רביעי של $date1',
hsb={'poslednja štwórćina $date1', case={'gen',''}},
hr='četvrta četvrtina $date1',
it='quarto quarto del $date1',
ja='$date1第4四半期',
ml='$date1 നാലാം ത്രൈമാസം',
nb='fjerde kvartal av $date1',
nl='vierde kwart $date1',
nn='tredje kvartalet av $date1',
no='fjerde kvartal av $date1',
pl={'czwarta ćwiartka $date1', case={'gen',''}},
qu='$date1 qhipaq ñiqin tawa phatmanpi',
ru={'последняя четверть $date1', case={'gen',''}},
sk={'4. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'zadnja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='fjärde kvartalet av $date1',
uk={'4-та чверть $date1', case={'gen',''}},
ur='چوتھی سہ ماہی $date1',
vi='quý 4 năm $date1',
['zh-hans']='$date1第四季度',
['zh-hant']='$date1第四季度',
default='<span style="color:red">forth quarter of $date1</span>',
},
-- ==========================================
-- === Seasons ==============================
-- ==========================================
['spring'] = {
nDates = 1,
ar='ربيع $date1',
['be-tarask']='вясна $date1',
bn='বসন্তকাল $date1',
ca='primavera $date1',
ckb='بەھاری $date1',
cs={'jaro $date1', case={'gen', ''}},
da='Forår $date1',
de='Frühling $date1',
en='Spring $date1',
['en-gb']='Spring $date1',
['en-ca']='Spring $date1',
eo='printempo $date1',
es='primavera de $date1',
et='$date1 kevad',
fa='بهار $date1',
fi='Kevät $date1',
fr='printemps $date1',
gl='primavera $date1',
he='אביב $date1',
hr='proljeće $date1',
hsb='nalěćo $date1',
hu='$date1 tavasza',
it='primavera $date1',
ja='{{#if:$date1|$date1年}}春',
mk='пролет $date1',
ml='$date1 വസന്തം',
nb='våren $date1',
nds='Vörjohr $date1',
nn='våren $date1',
nl='voorjaar $date1',
no='våren $date1',
pl='wiosna $date1',
pt='primavera de $date1',
ro='primăvara lui $date1',
ru='весна $date1',
sk={'jar $date1', case={'gen', ''}},
sl='pomlad $date1',
sv='våren $date1',
uk='весна $date1',
ur='بہار $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}春季',
['zh-hant']='{{#if:$date1|$date1年}}春季',
default='<span style="color:red">Spring $date1</span>',
},
['summer'] = {
nDates = 1,
ar='صيف $date1',
['be-tarask']='лета $date1',
bn='গ্রীষ্মকাল $date1',
ca='estiu $date1',
ckb='ھاوینی $date1',
cs={'léto $date1', case={'gen', ''}},
da='Sommer $date1',
de='Sommer $date1',
en='Summer $date1',
['en-gb']='Summer $date1',
['en-ca']='Summer $date1',
eo='somero $date1',
es='verano de $date1',
et='$date1 suvi',
fa='تابستان $date1',
fi='Kesä $date1',
fr='été $date1',
gl='verán $date1',
he='קיץ $date1',
hr='ljeto $date1',
hsb='lěćo $date1',
hu='$date1 nyara',
it='estate $date1',
ja='{{#if:$date1|$date1年}}夏',
mk='лето $date1',
ml='$date1 വേനൽ',
nb='sommeren $date1',
nds='Sommer $date1',
nn='sumaren $date1',
nl='zomer $date1',
no='sommeren $date1',
pl='lato $date1',
pt='verão de $date1',
ro='vara lui $date1',
ru='лето $date1',
sk={'leto $date1', case={'gen', ''}},
sl='poletje $date1',
sv='sommaren $date1',
uk='літо $date1',
ur='گرما $date1',
vi='mùa hè năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}夏季',
['zh-hant']='{{#if:$date1|$date1年}}夏季',
default='<span style="color:red">Summer $date1</span>',
},
['autumn'] = {
nDates = 1,
ar='خريف $date1',
['be-tarask']='восень $date1',
bn='শরত্কাল $date1',
ca='tardor $date1',
ckb='پاییزی $date1',
cs={'podzim $date1', case={'gen', ''}},
da='Efterår $date1',
de='Herbst $date1',
en='Autumn $date1',
['en-gb']='Autumn $date1',
['en-ca']='Autumn $date1',
eo='aŭtuno $date1',
es='otoño de $date1',
et='$date1 sügis',
fa='پاییز $date1',
fi='Syksy $date1',
fr='automne $date1',
gl='outono $date1',
he='סתיו $date1',
hr='jesen $date1',
hsb='nazyma $date1',
hu='$date1 ősze',
it='autunno $date1',
ja='{{#if:$date1|$date1年}}秋',
mk='есен $date1',
ml='$date1 ശരത്കാലം',
nb='høsten $date1',
nds='Harvst $date1',
nn='hausten $date1',
nl='herfst $date1',
no='høsten $date1',
pl='jesień $date1',
pt='outono de $date1',
ro='toamna lui $date1',
ru='осень $date1',
sk={'jeseň $date1', case={'gen', ''}},
sl='jesen $date1',
sv='hösten $date1',
uk='осінь $date1',
ur='خزاں $date1',
vi='mùa thu năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}秋季',
['zh-hant']='{{#if:$date1|$date1年}}秋季',
default='<span style="color:red">Autumn $date1</span>',
},
['winter'] = {
nDates = 1,
ar='شتاء $date1',
['be-tarask']='зіма $date1',
bn='শীতকাল $date1',
ca='hivern $date1',
ckb='زستانی $date1',
cs={'zima $date1', case={'gen', ''}},
da='Vinter $date1',
de='Winter $date1',
en='Winter $date1',
['en-gb']='Winter $date1',
['en-ca']='Winter $date1',
eo='vintro $date1',
es='invierno de $date1',
et='$date1 talv',
fa='زمستان $date1',
fi='Talvi $date1',
fr='hiver $date1',
gl='inverno $date1',
he='חורף $date1',
hr='zima $date1',
hsb='zyma $date1',
hu='$date1 tele',
it='inverno $date1',
ja='{{#if:$date1|$date1年}}冬',
mk='зима $date1',
ml='$date1 ശൈത്യകാലം',
nds='Winter $date1',
nl='winter $date1',
nb='vinteren $date1',
nn='vinteren $date1',
no='vinteren $date1',
pl='zima $date1',
pt='inverno de $date1',
ro='iarna lui $date1',
ru='зима $date1',
sk={'zima $date1', case={'gen', ''}},
sl='zima $date1',
sv='vintern $date1',
uk='зима $date1',
ur='سرما $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}冬季',
['zh-hant']='{{#if:$date1|$date1年}}冬季',
default='<span style="color:red">Winter $date1</span>',
},
-- ==========================================
-- === Decade/Century/Millennium ============
-- ==========================================
['decade'] = {
nDates = 1,
ar='أعوام $date1',
['be-tarask']='$date1-я гады',
bn='$date1-এর দশক',
ca='dècada del $date1',
ckb='دەیەی $date1',
cs=function(date1, state) return ((date1 % 100 == 0) and 'první dekáda' or ((date1 % 100) .. '. léta')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. století' end,
da="$date1'erne",
de='$date1er-Jahre',
el='δεκαετία του $date1',
en='$date1s',
['en-gb']='$date1s',
['en-ca']='$date1s',
eo='$date1-aj jaroj',
es='años $date1',
et='$date1ndad',
fa='دههٔ $date1',
fi='$date1-luvulla',
fr='années $date1',
gl='anos $date1',
he='שנות ה-$date1',
hr='$date1-ih',
hsb='$date1te lěta',
hu='$date1-s évek',
it="{{#ifexpr: $date1 mod 100 = 0 | primo decennio del {{Roman|{{#expr: ($date1 / 100) + 1}}}} sec. | anni '{{#expr: $date1 mod 100 }} del {{Roman|{{#expr: floor($date1 / 100) + 1}}}} sec.}}",
ja='$date1年代',
mk='$date1-ти',
ml='ദശകം $date1-ൽ',
nb='$date1-årene',
nn='$date1-åra',
nds='Johren $date1',
nl='jaren $date1',
no='$date1-årene',
pl=function(date1, state)
local str = 'lata '..(date1 % 100)..'. '
if (date1 % 100 == 0) then
str = 'pierwsza dekada '
elseif (date1 % 100 == 10) then
str = 'druga dekada '
end
return str .. Roman(math.floor(date1 / 100) + 1) .. ' wieku'
end,
pt='década de $date1',
qu='$date1 watakuna',
ro='anii $date1',
ru='$date1-е годы',
sk=function(date1, state) return ((date1 % 100 == 0) and 'prvé desaťročie' or ((date1 % 100) .. '. roky')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. storočia' end,
sl='$date1-ta leta',
sv='$date1-talet{{#switch:$date1|1300|1400|1500|1600|1700|1800|1900|2000|2100= (decennium)}}',
uk='$date1-ті роки',
ur='دہائی $date1',
vi='thập niên $date1',
['zh-hans']='$date1年代',
['zh-hant']='$date1年代',
default='<span style="color:red">$date1s</span>',
},
['century'] = {
nDates = 1,
ar='القرن $date1',
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' стагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' শতাব্দী' end,
ca='segle {{small-caps|{{lc:{{Roman year|$date1}}}}}}',
ckb='سەدەی $date1',
cs='$date1. století',
da='$date1. århundrede',
de='$date1. Jahrhundert',
el='$date1ος αιώνας',
en=function(date1, state) return Ordinal(date1, 'en') .. ' century' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' century' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' century' end,
eo='$date1-a jarcento',
es=function(date1, state) return 'siglo ' .. Roman(date1) end,
et='$date1. sajand',
fa='سدهٔ $date1',
fi='{{#expr: ($date1 - 1) * 100}}-luvulla',
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle' end,
gl=function(date1, state) return 'século ' .. Roman(date1) end,
he='המאה ה-$date1',
hr='$date1. stoljeće',
hsb='$date1. lětstotk',
hu='$date1. évszázad',
it=function(date1, state) return Roman(date1) .. ' sec.' end,
ja='$date1世紀',
la='saeculum $date1',
mk=function(date1, state) return Roman(date1) .. ' век' end,
ml='$date1-ആം നൂറ്റാണ്ട്',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' århundre' end,
nn=function(date1, state) return 'det ' .. Ordinal(date1, 'no') .. ' århundre' end,
nds='$date1. Johrhunnert',
nl='$date1e eeuw',
no=function(date1, state) return Ordinal(date1, 'no') .. ' århundre' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decCentury('pl',state.case) end,
pt=function(date1, state) return 'século ' .. Roman(date1) end,
qu='$date1 ñiqin pachakwata',
ro='secolul al $date1-lea',
ru=function(date1, state) return Roman(date1) .. ' век' end,
sk='$date1. storočie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. stoletje' end,
sv='{{#expr:$date1 - 1}}00-talet',
tt=function(date1, state) return Roman(date1) .. ' гасыр' end,
uk=function(date1, state) return Roman(date1) .. ' ст.' end,
ur='صدی $date1',
vi='thế kỷ $date1',
['zh-hans']='$date1世纪',
['zh-hant']='$date1世紀',
default='<span style="color:red">$date1 century</span>',
},
['millennium'] = {
nDates = 1,
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' тысячагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' সহস্রাব্দ' end,
ckb=function(date1, state) return 'ھەزارەی ' .. Ordinal(date1, 'ckb') end,
cs='$date1. tisíciletí',
de='$date1. Jahrtausend',
en=function(date1, state) return Ordinal(date1, 'en') .. ' millennium' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' millennium' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' millennium' end,
eo='$date1-a jarmilo',
es=function(date1, state) return Roman(date1) .. ' milenio' end,
fa=function(date1, state) return 'هزارهٔ ' .. Ordinal(date1, 'fa') end,
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' millénaire' end,
hr='$date1. tisućljeće',
hsb='$date1. lěttysac',
it=function(date1, state) return Roman(date1) .. ' millennio' end,
ja='$date1千年紀',
la='millennium $date1',
ml='$date1-മത്തെ സഹസ്രാബ്ദം',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' årtusen' end,
nn=function(date1, state) return Ordinal(date1, 'no') .. ' tusenår' end,
nl='$date1e millennium',
no=function(date1, state) return Ordinal(date1, 'no') .. ' årtusen' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decMillennium('pl',state.case) end,
pt=function(date1, state) return Roman(date1) .. ' milénio' end,
qu='$date1 ñiqin waranqawata',
ru=function(date1, state) return Roman(date1) .. ' тысячелетие' end,
sk='$date1. tisícročie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. tisočletje' end,
sv=function(date1, state) return Ordinal(date1, 'sv') .. ' årtusendet' end,
uk=function(date1, state) return Roman(date1) .. ' тис.' end,
ur=function(date1, state) return 'ہزارہ ' .. Ordinal(date1, 'ur') end,
vi='thiên niên kỷ $date1',
['zh-hans']=function(date1, state) return Ordinal(date1, 'zh-hans') .. '千年' end,
['zh-hant']=function(date1, state) return Ordinal(date1, 'zh-hant') .. '千年' end,
--default='{{Please translate|$date1 millennium|Module:I18n/complex date}}',
default='<span style="color:red">$date1 millennium</span>',
},
['million'] = {
ckb = '$date1 میلیۆن ساڵ',
de = '$date1 Millionen Jahre',
en = '$date1 million years',
['en-gb'] = '$date1 million years',
['en-ca'] = '$date1 million years',
eo = '$date1 milionoj da jaroj',
es = '$date1 millones de años',
nl = '$date1e miljoen jaar',
fr = '$date1 millions d’années',
it = '$date1 milioni di anni',
ml = function(date1, state)
if date1 >= 10 then
return (date1 / 10) .. ' കോടി വർഷങ്ങൾ' -- 100 lakh = 1 crore
else
return (date1 * 10) .. ' ലക്ഷം വർഷങ്ങൾ'
end
end,
nb = '$date1 millioner år',
nl = '$date1 miljoen jaar',
nn = '$date1 millionar år',
pl = '$date1 milionów lat',
pt = '$date1 milhões de anos',
ru = '$date1 миллиона лет',
uk = '$date1 мільйони років',
ur = '$date1 ملین سال',
['zh-hans'] = '$date1百万年',
['zh-hant'] = '$date1百万年',
default='<span style="color:red">$date1 million years</span>',
nDates = 1,
},
['billion'] = {
ckb = '$date1 بیلیۆن ساڵ',
de = '$date1 Milliarden Jahre',
en = '$date1 billion years',
['en-gb'] = '$date1 billion years',
['en-ca'] = '$date1 billion years',
es = '$date1 mil millones de años',
nl = '$date1e miljard jaar',
fr = '$date1 milliard d’années',
it = '$date1 miliardi di anni',
ml = function(date1, state) return (date1 * 10) .. ' കോടി വർഷങ്ങൾ' end,
nb = '$date1 milliarder år',
nl = '$date1 miljard jaar',
nn = '$date1 milliardar år',
pl = '$date1 miliardów lat',
pt = '$date1 mil milhões de anos',
ru = '$date1 миллиардов лет',
uk = '$date1 мільярдів років',
ur = '$date1 بلین سال',
['zh-hans'] = '$date1十亿年',
['zh-hant'] = '$date1十億年',
default='<span style="color:red$date1 billion years</span>',
nDates = 1,
},
-- ==========================================
-- === Complex phrases ======================
-- ==========================================
-- Phrases which for some languages can be build out of simpler phrases
-- Their "default" state, used when no translation in the requested language was found, returns a "state" like
-- table which is used to call the function again and built the phrase out of simple phrases
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the century'] = {
nDates = 2,
['be-tarask']=function(date1, date2, state) return 'канец ' .. Roman(date1) ..' — пачатак ' .. Roman(date2) ..' стагодзьдзя' end,
bn=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'bn') .. ' শতাব্দীর পালাবদলে' end,
ckb=function(date1, date2, state) return 'سەردەمی $date1/' .. Ordinal(date2, 'ckb') .. ' سەدە' end,
cs='přelom $date1. a $date2. století',
de=function(date1, date2, state) return 'Jahrhundertwende $date1/' .. Ordinal(date2, 'de') end,
en=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en') .. ' century' end,
['en-gb']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-gb') .. ' century' end,
['en-ca']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-ca') .. ' century' end,
eo='ŝanĝo de la $date1-a al la $date2-a jarcentoj',
es=function(date1, date2, state) return 'hacia finales del siglo ' .. Roman(date1) .. ' y principios del siglo ' .. Roman(date2) .. '' end,
fi='{{#expr: $date1 * 100-100}}-luvun loppu ja {{#expr: $date2 * 100-100}}-luvun alku',
fr=function(date1, date2, state) return 'fin du ' .. Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle / début du ' .. Ordinal(date2, 'fr', 'roman', 'm') end,
hr='kraj $date1. i početak $date2. stoljeća',
hsb=function(date1, date2, state) return 'přechod ' .. Roman(date1) ..' do ' .. Roman(date2) ..' lětstotka' end,
it=function(date1, date2, state) return 'fine del ' .. Roman(date1) .. ' sec. e inizio del ' .. Roman(date2) .. ' sec.' end,
ja='$date1世紀から$date2世紀への変わり目',
ml='$date1 നൂറ്റാണ്ട് അവസാനവും $date2 നൂറ്റാണ്ട് ആദ്യവും',
nb=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nb') .. ' århundre/begynnelsen av det ' .. Ordinal(date2, 'nb') .. ' århundre' end,
nl=function(date1, date2, state) return 'eind $date1e/begin $date2e eeuw' end,
nn=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nn') .. ' hundreåret/byrjinga av det ' .. Ordinal(date2, 'nn') .. ' hundreåret' end,
pl=function(date1, date2, state) return 'przełom ' .. Roman(date1) ..' i ' .. Roman(date2) ..' wieku' end,
qu="$date1 ñiqin waranqawatamanta $date2 ñiqin waranqawataman t'ikrasqanpi",
ru=function(date1, date2, state) return 'конец ' .. Roman(date1) ..' — начало ' .. Roman(date2) ..' века' end,
sk='prelom $date1. a $date2. storočia',
sl=function(date1, date2, state) return 'pozno ' .. Ordinal(date1, 'sl') .. '. in zgodnje ' .. Ordinal(date2, 'sl') .. '. stoletje' end,
sv=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'sv') .. ' årtusendets sekelskifte' end,
uk=function(date1, date2, state) return 'кін. ' .. Roman(date1) ..' ст. — поч. ' .. Roman(date2) ..' ст.' end,
vi='bước sang thế kỷ $date2',
['zh-hans']=function(date1, date2, state) return '$date1世纪末' .. Ordinal(date2, 'zh-hans') .. '世纪初' end,
['zh-hant']=function(date1, date2, state) return '$date1世紀末' .. Ordinal(date2, 'zh-hant') .. '世紀初' end,
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='century', era1=state.era1, adj2 = 'early', unit2='century', era2=state.era2} end,
},
-- $date1 - original date - 10
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the decade']={
nDates = 2,
['be-tarask']='канец $date1-х — пачатак $date2-х гг.',
bn='$date1-এর দশকের শেষে এবং $date2-এর দশকের শুরুতে',
cs=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "přelom 1. a 2. dekády " .. century1 .. ". století"
elseif decade2 == 0 and date2 < date1 then
return "přelom 9. a 10. dekády " .. century1 .. ". století"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "přelom " .. century1 .. ". a " .. century2 .. ". století"
else
return "přelom " .. decade1 .. "0. a " .. decade2 .. "0. let " .. century1 .. ". století"
end
end,
de='späte $date1er und frühe $date2er',
ckb='کۆتایییەکانی $date1 و سەرەتاکانی $date2',
en='late $date1s and early $date2s',
['en-gb']='late $date1s and early $date2s',
['en-ca']='late $date1s and early $date2s',
eo='ŝanĝo de la $date1-a al la $date2-a jardekoj',
es='finales de los años $date1 y principios de los años $date2',
fa='اواخر دههٔ $date1 و اوایل دههٔ $date2',
fi='$date1-luvun loppu ja $date2-luvun alku',
fr='fin des années $date1 / début des années $date2',
hr='kraj $date1-ih i početak $date2-ih',
hsb='pózdnje $date1te a zažne $date2te lěta',
it='fine degli anni $date1 / inizio degli anni $date2',
ja='$date1年代末から$date2年代初頭',
ml='$date1കൾ അവസാനവും $date2കൾ ആദ്യവും',
nb = 'sent på $date1-tallet og tidlig på $date2-tallet',
nl = 'eind $date1e/begin $date2e decennium',
nn = 'seint på $date1-talet og tidleg på $date2-talet',
pl=function(date1, date2, state)
local decade1 = math.floor(date1 % 100)
local decade2 = math.floor(date2 % 100)
local century1 = Roman(math.floor( date1 / 100) + 1)
local century2 = Roman(math.floor( date2 / 100) + 1)
if decade1 == 0 and date1 < date2 then
return "przełom pierwszej i drugiej dekady " .. century1 .. " wieku"
elseif decade1 == 10 and date1 < date2 then
return "przełom drugiej dekady i lat 20. " .. century1 .. " wieku"
elseif decade1 == 90 and decade2 == 0 then
return "przełom ostatniej dekady " .. century1 .. " i pierwszej dekady " .. century2 .. " wieku"
else
return "przełom lat " .. decade1 .. ". i " .. decade2 .. ". " .. century1 .. " wieku"
end
end,
qu='qhipaq $date1 watakuna ñawpaq $date2 watakunapas',
ru='конец $date1-х — начало $date2-х гг.',
sv='sena $date1-talet och tidiga $date2-talet',
sk=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "prelom 1. a 2. desaťročia " .. century1 .. ". storočia"
elseif decade2 == 0 and date2 < date1 then
return "prelom 9. a 10. desaťročia " .. century1 .. ". storočia"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "prelom " .. century1 .. ". a " .. century2 .. ". storočia"
else
return "prelom " .. decade1 .. "0. a " .. decade2 .. "0. rokov " .. century1 .. ". storočia"
end
end,
sl='pozna $date1-ta in zgodnja $date2-ta leta',
uk='кінець $date1-х — початок $date2-х рр.',
ur='اخیر دہائی $date1 و ابتدا دہائی $date2',
vi='bước sang thập niên $date2',
['zh-hans']='$date1年代末$date2年代初',
['zh-hant']='$date1年代末$date2年代初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='decade', era1=state.era1, adj2 = 'early', unit2='decade', era2=state.era2} end,
},
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the year']={
nDates = 2,
['be-tarask']='канец $date1 — пачатак $date2 г.',
bn='$date1-এর শেষে এবং $date2-এর শুরুতে',
ckb='کۆتاییی $date1 و سەرەتای $date2',
cs='přelom roku $date1/$date2',
de='Jahreswechsel $date1/$date2',
en='late $date1 and early $date2',
['en-gb']='late $date1 and early $date2',
['en-ca']='late $date1 and early $date2',
eo='jarŝanĝo $date1 / $date2',
es='finales de $date1 y principios de $date2',
fa='اواخر $date1 و اوایل $date2',
fi='vuoden $date1 loppu ja vuoden $date2 alku',
fr='fin $date1 / début $date2',
hsb='kónc $date1/spočatk $date2',
ja='$date1年末から$date2年初め',
ml='$date1 അവസാനവും $date2 ആദ്യവും',
nb='årsskiftet $date1/$date2',
nl='eind $date1 en begin $date2',
nn='årsskiftet $date1/$date2',
pl='koniec $date1 i początek $date2 roku',
qu="$date1 qhipaq p'unchawkuna $date2 ñawpaq p'unchawkunapas",
ru='конец $date1 — начало $date2 г.',
sk='prelom rokov $date1/$date2',
sl='konec leta $date1 in začetek $date2',
sv='sena $date1 och tidiga $date2',
uk='кінець $date1 — початок $date2 р.',
ur='اخیر $date1 و ابتدا $date2',
vi='bước sang năm $date2',
['zh-hans']='$date1年末$date2年初',
['zh-hant']='$date1年末$date2年初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='year', era1=state.era1, adj2 = 'early', unit2='year', era2=state.era2} end,
},
['circa2'] = {
nDates = 2,
ar='حوالي $date1 – $date2',
['be-tarask']={'прыкладна з $date1 да прыкладна $date2', case={'gen','gen'}},
bn='প্রায় $date1 এবং প্রায় $date2-এর মাঝামাঝি',
ca='cap a $date1 o $date2',
ckb='بە نزیکی لەنێوان $date1 و $date2',
cs='cca $date1 až $date2',
da='ca. mellem $date1 og $date2',
de='etwa $date1 bis $date2',
el='περίπου μεταξύ $date1 και $date2',
en='between c. $date1 and c. $date2',
['en-gb']='between circa $date1 and circa $date2',
['en-ca']='between circa $date1 and circa $date2',
eo='proksimume de $date1 ĝis $date2',
es='hacia $date1 o $date2',
et='ca. $date1 ja $date2 vahel',
fa='حدوداً بین $date1 و $date2',
fi={function(date1, date2, state) return 'suunnilleen ' .. decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='vers $date1-$date2',
gl='entre $date1 e $date2',
he='בסביבות ה- $date1 עד $date2',
hr='oko između $date1 i $date2',
hsb={'wot něhdźe $date1 do něhdźe $date2', case={'gen','gen'}},
hu='$date1 és $date2 között',
it='tra il $date1 e il $date2 circa',
ja='$date1頃と$date2頃の間',
mk='~ помеѓу $date1 и $date2',
ml='ഉദ്ദേശം $date1-നും ഉദ്ദേശം $date2-നും ഇടയ്ക്ക്',
nb='ca. mellom $date1 og $date2',
nn='ca. mellom $date1 og $date2',
nds='üm un bi $date1 bet $date2',
nl='ca. $date1 tot $date2',
no='ca. mellom $date1 og $date2',
pl={'od około $date1 do około $date2', case={'gen','gen'}},
pt='cerca de $date1 a $date2',
qu="$date1 p'unchawmantachá $date2 p'unchawkamachá",
ro='circa $date1 – $date2',
ru={'с около $date1 до, примерно, $date2', case={'gen','gen'}},
sk='cca $date1 až $date2',
sl={function(date1, date2, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1 do $date2' end, case={'gen','gen'}},
sv='c:a $date1 till $date2',
uk={'з близько $date1 до, приблизно, $date2', case={'gen','gen'}},
ur='قریباً بین $date1 و $date2',
vi='giữa vào khoảng $date1 và vào khoảng $date2',
['zh-hans']='约$date1~$date2间',
['zh-hant']='約$date1~$date2間',
default=function(date1, date2, state) return {conj='between', adj1 = 'circa', unit1=state.unit1, era1=state.era1, adj2 = 'circa', unit2=state.unit2, era2=state.era2} end,
},
-- ==========================================
-- === Eras and calendars ===================
-- ==========================================
['julian'] = {
nDates = 2,
ar='$date1 ($date2 حسب [[w:ar:التقويم اليولياني|تقويم يولياني]])',
['be-tarask']='$date1 ($date2 паводле [[w:be-tarask:Юліянскі каляндар|ст. ст.]]',
bn='$date1 (জুলিয়ান ক্যালেন্ডারে $date2)',
ca='$date1 ($date2 segons el [[w:ca:Calendari julià|calendari julià]])',
ckb='$date1 ($date2 بەپێی [[w:ڕۆژژمێری جولیانی|ڕۆژژمێری جولیانی]])',
cs='$date1 ($date2 podle [[w:cs:Juliánský kalendář|juliánského kalendáře]])',
da='$date1 ($date2 [[w:da:Julianske kalender|Julianske kalender]])',
de='$date1 ($date2 [[w:de:Julianischer Kalender|Julianischer Kalender]])',
el='$date1 ($date2 με το [[w:el:Ιουλιανό ημερολόγιο|Ιουλιανό ημερολόγιο]])',
en='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-gb']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-ca']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
fa='$date1 ($date2 در [[w:fa:Old Style and New Style dates|تقویم جولیان]])',
eo='$date1 ($date2 laŭ la [[w:eo:Julia kalendaro|Julia Kalendaro]])',
es='$date1 ($date2 según el [[w:es:Calendario juliano|calendario juliano]])',
et='$date1 ($date2 [[w:et:Juliuse kalender|Juliuse kalendris]])',
fi='$date1 ($date2 [[w:fi:juliaaninen kalenteri|juliaanisen kalenterin]] mukaan)',
fr='$date1 ($date2 dans le [[w:fr:calendrier julien|calendrier julien]])',
gl='$date1 ($date2 segundo o [[w:gl:Calendario xuliano|calendario xuliano]])',
hu='$date1 ($date2 a [[w:hu:Julianus-naptár|Julián naptárban]])',
hr='$date1 ($date2 prema [[w:hr:Julijanski kalendar|julijanskom kalendaru]])',
he='$date1 ($date2 ב[[w:he:לוח היוליאני|הלוח היוליאני]])',
hsb='$date1 ($date2 po julianiskej protyce)',
it='$date1 ($date2 secondo il [[w:it:Calendario giuliano|calendario giuliano]])',
ja='$date1([[w:ja:ユリウス暦|ユリウス暦]] $date2)',
mk='$date1 ($date2 по [[w:mk:Јулијански календар|Јулијанскиот календар]])',
ml='$date1 ([[w:ml:ജൂലിയൻ കാലഗണനാരീതി|ജൂലിയൻ വർഷത്തിൽ]] $date2)',
ml='$date1 ([[w:Old Style and New Style dates|ജൂലിയൻ കാലഗണനാരീതിയിൽ]] $date2)',
nds='$date1 ($date2 na’n [[w:nds:Juliaansch Klenner|Juliaanschen Klenner]])',
nb='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nn='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nl='$date1 ($date2 volgens de [[w:nl:Juliaanse kalender|Juliaanse kalender]])',
no='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalender]])',
pl='$date1 ($date2 wedle [[w:pl:Daty nowego i starego porządku|kalendarza juliańskiego]])',
pt='$date1 ($date2 no [[w:pt:Mudança para o calendário gregoriano|calendário juliano]])',
qu='$date1 ($date2 [[w:qu:Hulyanu kalindaryu|Hulyanu kalindaryupi]])',
ro='$date1 ($date2 în [[w:ro:calendarul iulian|calendarul iulian]])',
ru='$date1 ($date2 [[w:ru:Юлианский календарь|по ст. ст.]])',
sk='$date1 ($date2 podľa [[w:sk:Juliánsky kalendár|juliánskeho kalendára]])',
sl='$date1 ($date2 po [[w:sl:Julijanski koledar|julijanskem koledarju]])',
sv='$date1 ($date2 [[w:sv:Gamla stilen|gamla stilen]])',
uk='$date1 ($date2 [[w:uk:Юліанський календар|Юліанський календар]])',
ur='$date1 ($date2 حسب [[w:ur:نئے اور پانے انداز کی تاریخ|جولین تقویم]])',
vi='$date1 ($date2 trong [[w:vi:Lịch Julius|lịch Juliêng]])',
['zh-hans']='$date1([[w:Old Style and New Style dates|格里历]]$date2)',
['zh-hant']='$date1([[w:Old Style and New Style dates|格里曆]]$date2)',
default='<span style="color:red">$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])</span>',
},
['ad'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:ад Нараджэньня Хрыста|пасьля Н. Х.]]',
bn ='$date1 খ্রিস্টাব্দ',
ca ='$date1 dC',
ckb ='$date1 [[w:ckb:ئانۆ دۆمینی|پاز]]',
cs ='$date1 n. l.',
de ='$date1 n. Chr.',
el ='$date1 μ.Χ.',
en ='$date1 [[w:en:Anno Domini|AD]]',
['en-gb'] ='$date1 [[w:en:Anno Domini|AD]]',
['en-ca'] ='$date1 [[w:en:Anno Domini|AD]]',
eo ='$date1 pK',
es ='$date1 [[w:es:Anno Dómini|d. C.]]',
fa ='$date1 م.',
fi ='$date1 jaa.',
fr ='$date1 ap. J.-C.',
gl ='$date1 d.C.',
he ='$date1 לספירה',
hu ='i. sz. $date1',
hr ='$date1 p. Kr.',
hsb='$date1 po Chr.',
it ='$date1 d.C.',
ja ='西暦$date1',
mk ='$date1 н.е.',
ml ='ക്രി.വ. $date1',
nb ='$date1 e.Kr.',
nl ='$date1 n.Chr.',
nn ='$date1 e.Kr.',
no ='$date1 e.Kr.',
pl ='$date1 [[w:pl:naszej ery|n.e.]]',
ro ='$date1 d.Hr.',
ru ='$date1 н. э.',
sk ='$date1 po Kr.',
sl ='$date1 n. št.',
sv ='$date1 [[w:sv:Efter Kristus|e.Kr.]]',
uk ='$date1 н. е.',
ur ='$date1 ء',
vi ='$date1 [[w:vi:Công Nguyên|CN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元]]$date1',
default='<span style="color:red">$date1 [[w:en:Anno Domini|AD]]</span>',
},
['ah'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:Ісламскі каляндар|хіджры]]',
bn ='$date1 [[w:bn:ইসলামি বর্ষপঞ্জি|হিজরী]]',
ckb ='$date1 [[w:ckb:ساڵی کۆچی|سک]]',
cs ='$date1 [[w:cs:Anno Hegirae|AH]]',
en ='$date1 [[w:en:Hijri year|AH]]',
['en-gb'] ='$date1 [[w:en:Hijri year|AH]]',
['en-ca'] ='$date1 [[w:en:Hijri year|AH]]',
eo ='$date1 post la [[w:eo:Heĝiro|Heĝiro]]',
fa ='$date1 ه.ق.',
fr ='$date1 [[w:fr:Calendrier musulman|A.H.]]',
he ="$date1 [[w:he:הלוח המוסלמי|להג'רה]]",
hr ='$date1 god. hidžre',
ja ='[[w:ja:ヒジュラ暦|ヒジュラ暦]]$date1',
ml ='$date1 [[w:ml:ഇസ്ലാമിക കലണ്ടർ|ഹിജ്റ]]',
nb ='$date1 [[w:no:Den muslimske kalenderen|A.H.]]',
nn ='$date1 [[w:nn:Den muslimske kalenderen|A.H.]]',
pl ='$date1 [[w:pl:Kalendarz muzułmański|A.H.]]',
ru ='$date1 [[w:ru:Исламский календарь|хиджры]]',
sk ='$date1 [[w:sk:A. H.|A. H.]]',
sv ='$date1 [[w:sv:Muslimska kalendern|A.H.]]',
uk ='$date1 [[w:uk:Мусульманський календар|хіджри]]',
ur ='$date1 ھ',
vi ='$date1 [[w:vi:Lịch Hồi giáo|AH]]',
['zh-hans'] ='[[w:en:Hijri year|Hijri历]]$date1',
['zh-hant'] ='[[w:en:Hijri year|Hijri曆]]$date1',
default='<span style="color:red">$date1 [[w:en:Hijri year|AH]]</span>',
},
['bc'] = {
nDates = 1,
ar ='$date1 ق.م.',
['be-tarask']='$date1 [[w:be-tarask:да Нараджэньня Хрыста|да Н. Х.]]',
bn ='খ্রিস্টপূর্ব $date1',
ca ='$date1 [[w:ca:Abans de Crist|aC]]',
ckb ='$date1 [[w:ckb:پێش زایین|پز]]',
cs ='$date1 [[w:cs:Př. n. l.|př. n. l.]]',
de ='$date1 [[w:de:v. Chr.|v. Chr.]]',
el ='$date1 π.Χ.',
en ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-gb'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-ca'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
eo ='$date1 aK',
es ='$date1 [[w:es:Antes de Cristo|a. C.]]',
fa ='$date1 پ.م.',
fi ='$date1 eaa.',
fr ='$date1 av. J.-C.',
gl ='$date1 [[w:gl:Antes de Cristo|a.C.]]',
he ='$date1 לפני הספירה',
hr ='$date1 [[w:hr:Pr. Kr.|pr. Kr.]]',
hsb='$date1 do Chr.',
hu ='i. e. $date1',
it ='$date1 [[w:it:Avanti Cristo|a.C.]]',
ja ='[[w:ja:紀元前|紀元前]]$date1',
mk ='$date1 п.н.е.',
ml ='ക്രി.മു. $date1',
nds='$date1 vör uns Tied',
nb ='$date1 f.Kr.',
nl ='$date1 v.Chr.',
nn ='$date1 f.Kr.',
no ='$date1 f.Kr.',
pl ='$date1 [[w:pl:przed naszą erą|p.n.e.]]',
ro ='$date1 î.Hr.',
ru ='$date1 до н. э.',
sk ='$date1 [[w:sk:Pred Kristom|pred Kr.]]',
sl ='$date1 pr. n. št.',
sv ='$date1 [[w:sv:Före Kristus|f.Kr.]]',
uk ='$date1 до н. е.',
ur ='$date1 ق م',
vi ='$date1 [[w:vi:Trước Công Nguyên|TCN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元前]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元前]]$date1',
default='<span style="color:red">$date1 [[w:en:Ante Christum Natum|BC]]</span>',
},
['bp'] = {
nDates = 1,
ar ='$date1 [[w:ar:قبل الحاضر|قبل الحاضر]]',
be ='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
['be-tarask']='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
bn ='বর্তমানের আগে $date1',
ca ='$date1 [[w:ca:BP (unitat de temps)|BP]]',
ckb ='$date1 [[w:ckb:پێش ئێستا|پێش ئێستا]]',
cs ='$date1 [[w:cs:B. P.|B. P.]]',
de ='$date1 [[w:de:Before Present|BP]]',
el ='$date1 [[w:en:Before Present|BP]]',
en ='$date1 [[w:en:Before Present|BP]]',
['en-gb'] ='$date1 [[w:en:Before Present|BP]]',
['en-ca'] ='$date1 [[w:en:Before Present|BP]]',
eo='$date1 [[w:eo:Before Present|BP]]',
es ='$date1 [[w:es:Before Present|BP]]',
et ='$date1 [[w:et:Before Present|BP]]',
fa ='$date1 پیش از اکنون',
fi ='$date1 [[w:fi:BP (ajanlasku)|BP]]',
fr ='$date1 [[w:fr:Avant le présent|avant le présent]]',
hr ='$date1 [[w:hr:BP|prije današnjice]]',
it ='$date1 [[w:it:Before Present|BP]]',
ja ='$date1 [[w:ja:BP (年代測定)|BP]]',
lv ='$date1 [[w:lv:Pirms mūsdienām|Pirms mūsdienām]]',
mk ='$date1 [[w:mk:Пред сегашноста|п.с.]]',
ml ='[[w:en:Before Present|ഇന്നത്തെ കാലത്തിനു മുമ്പ്]] $date1',
nb ='$date1 [[w:no:Before Present|BP]]',
nn ='$date1 [[w:nn:Before Present|BP]]',
nl ='$date1 [[w:nl:Before Present|BP]]',
no ='$date1 [[w:no:Before Present|BP]]',
pl ='$date1 [[w:pl:Before Present|BP]]',
pt ='$date1 [[w:pt:Antes do Presente|Antes do Presente]]',
ru ='$date1 [[w:ru:До настоящего времени|до н. в.]]',
sk ='$date1 [[w:sk:BP (čas)|BP]]',
sl ='$date1 [[w:sl:pred sedanjostjo|p. s.]]',
sr ='$date1 [[w:sr:Прије садашњости|Прије садашњости]]',
sv ='$date1 [[w:sv:B.P.|B.P.]]',
uk ='$date1 [[w:uk:Років тому|Років тому]]',
ur ='$date1 قبل حاضر',
vi ='$date1 [[w:en:Before Present|trước ngày nay]]',
['zh-hans'] ='[[w:zh:距今|距今]]$date1',
['zh-hant'] ='[[w:zh:距今|距今]]$date1',
default='<span style="color:red">$date1 [[w:en:Before Present|BP]]</span>',
},
}
-- List of possible string inputs to "adj", "junction", "era", and "precision" fields
-- Each term is matched to its synonym and the role: a - adjective, e - era
-- p - precision, c - calendar and combos, r - certainty and j - conjunction
p.Synonyms = {
-- adjectives and prepositions
['quarter1']={'1quarter','a'},
['1stquarter']={'1quarter','a'},
['1st quarter']={'1quarter','a'},
['first quarter']={'1quarter','a'},
['1quarter']={'1quarter','a'},
['quarter2']={'2quarter','a'},
['2ndquarter']={'2quarter','a'},
['2nd quarter']={'2quarter','a'},
['second quarter']={'2quarter','a'},
['2quarter']={'2quarter','a'},
['quarter3']={'3quarter','a'},
['3rdquarter']={'3quarter','a'},
['3rd quarter']={'3quarter','a'},
['third quarter']={'3quarter','a'},
['3quarter']={'3quarter','a'},
['quarter4']={'4quarter','a'},
['4thquarter']={'4quarter','a'},
['4th quarter']={'4quarter','a'},
['fourth quarter']={'4quarter','a'},
['4quarter']={'4quarter','a'},
['spring']={'spring','a'},
['summer']={'summer','a'},
['fall']={'autumn','a'},
['autumn']={'autumn','a'},
['winter']={'winter','a'},
['beginning']={'early','a'},
['early']={'early','a'},
['1half']={'firsthalf','a'},
['1sthalf']={'firsthalf','a'},
['1st half']={'firsthalf','a'},
['firsthalf']={'firsthalf','a'},
['first half']={'firsthalf','a'},
['end']={'late','a'},
['late']={'late','a'},
['middle']={'mid','a'},
['mid']={'mid','a'},
['2half']={'secondhalf','a'},
['2ndhalf']={'secondhalf','a'},
['2nd half']={'secondhalf','a'},
['secondhalf']={'secondhalf','a'},
['second half']={'secondhalf','a'},
['<']={'before','a'},
['b']={'before','a'},
['before']={'before','a'},
['>']={'after','a'},
['a']={'after','a'},
['after']={'after','a'},
['~']={'circa','a'},
['c']={'circa','a'},
['ca']={'circa','a'},
['circa']={'circa','a'},
['by']={'by','a'},
['from']={'from','a'},
['until']={'until','a'},
['uncertain']={'uncertain','r'},
['possibly']={'possibly','r'},
['probably']={'probably','r'},
['presumably']={'presumably','r'},
-- eras
['bc']={'bc','e'},
['bce']={'bc','e'},
['before present']={'bp','e'},
['bp']={'bp','e'},
['ce']={'ad','e'},
['ad']={'ad','e'},
['ah']={'ah','e'},
-- conjunctions
['&']={'and','j'},
['and']={'and','j'},
['or']={'or','j'},
['-']={'from-until','j'},
['–']={'from-until','j'},
['from-until']={'from-until','j'},
['between']={'between','j'},
['circa2']={'circa2','a'},
-- precision
['day']={'day','p'},
['month']={'month','p'},
['year']={'year','p'},
['s']={'decade','p'},
['decade']={'decade','p'},
['century']={'century','p'},
['millennium']={'millennium','p'},
['million']={'million','p'},
['billion']={'billion','p'},
-- calendar
['julian']={'julian','c'},
['islamic']={'islamic','c'},
['hijri']={'islamic','c'},
-- "Turn of the ..." options are a combos which will be handled in similar way as calendars
['turn of the century']={'turn of the century','c'},
['century turn']={'turn of the century','c'},
['turn of the decade']={'turn of the decade','c'},
['decade turn']={'turn of the decade','c'},
['turn of the year']={'turn of the year','c'},
['year turn']={'turn of the year','c'},
-- other dates which will be handled in similar way as calendars
['?']={'unknown','c'},
['unknown']={'unknown','c'},
['unknown year']={'year unknown','c'},
['year unknown']={'year unknown','c'},
}
return p
dq6g8vtfaua8zgt63ytoeyms0jwcsm1
803612
803611
2026-07-30T00:19:31Z
SM7
3953
6 revisions imported from [[:en:Module:I18n/complex_date]]
803611
Scribunto
text/plain
--[[
__ __ _ _ ___ _ ___ __ _ _ _
| \/ | ___ __| |_ _| | ___ _|_ _/ |( _ ) _ __ / /__ ___ _ __ ___ _ __ | | _____ __ __| | __ _| |_ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_)| || |/ _ \| '_ \ / / __/ _ \| '_ ` _ \| '_ \| |/ _ \ \/ / / _` |/ _` | __/ _ \
| | | | (_) | (_| | |_| | | __/_ | || | (_) | | | |/ / (_| (_) | | | | | | |_) | | __/> < | (_| | (_| | || __/
|_| |_|\___/ \__,_|\__,_|_|\___(_)___|_|\___/|_| |_/_/ \___\___/|_| |_| |_| .__/|_|\___/_/\_\ \__,_|\__,_|\__\___|
|_|
This code was copied from Commons please request changes there.
Maintainers:
*Jarekt
]]
local function Ordinal(...)
return require('Module:Ordinal-cd')._Ordinal(...)
end
local function Roman(...)
return require('Module:Roman-cd')._Numeral(...)
end
-- ==========================================================================
-- === Local Helper functions ==============================================
-- ==========================================================================
-- form of French "of" based on "precision", which is wikibase compatible date precision format:
-- 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
-- 1 2 3 4 5 mill cent deca year month day
local frOf = {'du','du','du','du','du', 'du', 'du', 'de', 'de', 'de', 'du'}
local function decYear(state,lang,case)
--Declension of word "year" in several languages
if state.precision == 9 then
yearCase = {
fi={xxx='vuosien', gen='vuoden', ill='vuoteen', ptv='vuotta', ela='vuodesta', ine='vuonna'},
sl={insP='letoma', ins='letom', gen='leta', loc='letu'},
hsb={insP='lětami', ins='lětom', gen='lěta', loc='lěće'}
}
return yearCase[lang][case] .. ' '
else
return ''
end
end
local function decCentury(lang,case)
--Declension of word "century" in several languages
centuryCase = {
pl={nom='wiek', gen='wieku', ins='wiekiem', loc='wieku', default = 'wiek'},
hsb={nom='lětstotk', gen='lětstotka', ins='lětstotkom', loc='lětstotku', default = 'lětstotk'}
}
return centuryCase[lang][case] or centuryCase[lang]['default']
end
local function decMillennium(lang,case)
--Declension of word "century" in several languages
millenniumCase = {
pl={nom='tysiąclecie', gen='tysiąclecia', ins='tysiącleciem', loc='tysiącleciu', default = 'tysiąclecie'},
hsb={nom='lěttysac', gen='lěttysaca', ins='lěttysacom', loc='lěttysacu', default = 'lěttysac'}
}
return millenniumCase[lang][case] or millenniumCase[lang]['default']
end
-- ==========================================================================
-- === Output data structure ===============================================
-- ==========================================================================
local p = {}
--[[
Multiple formats are possible:
1) string with key words "$date1" and "$date2" which will be replaced later by the date strings
2) function which can be used to create output string
* Function INPUTS:
- date1 and date2 string with date strings. "$date1" and "$date2" can also be used which will be replaced latter
- state is a table with full description of the phrase we are trying to construct. It includes state.precision1 (and 2)
which is wikibase compatible date precision format: 6=millennium, 7=century, 8=decade, 9=year, 10=month, 11=day
3) table with following fields:
* table[1] is either string or a function
* table.case is a 2 element table with Grammatical cases used by given language to alter
month name or other adjective related to date # 1 and 2
]]
p.Translations = {
-- ==========================================
-- === Two date phrases =====================
-- ==========================================
['from-until'] = {
nDates = 2,
ar='من $date1 إلى $date2',
['be-tarask']={'з $date1 да $date2', case={'gen','gen'}},
bn='$date1 থেকে $date2 পর্যন্ত',
ca='$date1 fins $date2',
ckb='لە $date1 تاوەکوو $date2',
cs='$date1 až $date2',
da='fra $date1 til $date2',
de='$date1 bis $date2',
el='από $date1 έως $date2',
en='from $date1 until $date2',
['en-gb']='from $date1 until $date2',
['en-ca']='from $date1 until $date2',
eo='de $date1 al $date2',
es='de $date1 a $date2',
et='$date1 kuni $date2',
fa='از $date1 تا $date2',
fi='$date1–$date2', -- välisenä aikana
fr=function(date1, date2, state) -- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision > 10 then -- precision > month
return 'du $date1 au $date2'
else -- "DE septebmbre" / "D'octobre"
return require("Module:Linguistic").of(date1, 'fr') .. " à $date2"
end
end,
gl='de $date1 a $date2', --Template:From until used 'desde $date1 ata $date2'
he='מ- $date1 עד $date2',
hr='od $date1 do $date2',
hsb={'wot $date1 do $date2', case={'gen','gen'}},
hu='$date1 – $date2',
it='dal $date1 al $date2',
ja='$date1から$date2まで',
mk='од $date1 до $date2',
ml='$date1 മുതൽ $date2 വരെ',
nb='fra $date1 til $date2',
nds='$date1 bet $date2',
nn='fra $date1 til $date2',
nl='$date1 tot $date2',
no='fra $date1 til $date2',
pl={'od $date1 do $date2', case={'gen','gen'}},
ro='din $date1 până în $date2',
pt='de $date1 a $date2',
qu="$date1 p'unchawmanta $date2 p\'unchawkama",
ru={'с $date1 по $date2', case={'gen','gen'}},
sk={'od $date1 do $date2', case={'gen','gen'}},
sl={'od $date1 do $date2', case={'gen','gen'}},
sv='från $date1 till $date2',
uk={'з $date1 до $date2', case={'gen','gen'}},
ur='از $date1 تا $date2',
vi='từ $date1 đến $date2',
['zh-hans']='从$date1到$date2',
['zh-hant']='從$date1到$date2',
default='<span style="color:red">$date1 - $date2</span>',
},
['between']={
nDates = 2,
ar='بين $date1 و $date2',
['be-tarask']={'між $date1 і $date2', case={'ins','ins'}},
bn='$date1 এবং $date2-এর মাঝামাঝি',
ca='entre $date1 i $date2',
ckb='لەنێوان $date1 و $date2',
cs={'mezi $date1 a $date2', case={'ins','ins'}},
da='mellem $date1 og $date2',
de='zwischen $date1 und $date2',
el='μεταξύ $date1 και $date2',
en='between $date1 and $date2',
['en-gb']='between $date1 and $date2',
['en-ca']='between $date1 and $date2',
eo='inter $date1 kaj $date2',
es='entre $date1 y $date2',
et='$date1 ja $date2 vahel',
fa='بین $date1 و $date2',
fi={function(date1, date2, state) return decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='entre $date1 et $date2',
gl='entre $date1 e $date2',
he='בין $date1 ו- $date2',
hr='između $date1 i $date2',
hsb={'mjez $date1 a $date2', case={'ins','ins'}},
hu='$date1 és $date2 között',
hi='$date1 और $date2 के बीच',
it='tra il $date1 e il $date2',
ja='$date1と$date2の間',
mk='помеѓу $date1 и $date2',
ml='$date1, $date2 എന്നിവയ്ക്കിടയ്ക്ക്',
nb='mellom $date1 og $date2',
nds='twischen $date1 un $date2',
nn='mellom $date1 og $date2',
nl='tussen $date1 en $date2',
no='mellom $date1 og $date2',
pl={'między $date1 a $date2', case={'ins','ins'}},
ro='între $date1 și $date2',
pt='entre $date1 e $date2',
qu="$date1 p'unchawpa $date2 p'unchawpapas chawpin",
ru={'между $date1 и $date2', case={'ins','ins'}},
sk={'medzi $date1 a $date2', case={'ins','ins'}},
sl={function(date1, date2, state) return 'med ' .. decYear(state,'sl','insP') .. '$date1 in $date2' end, case={'ins','ins'}},
sv='mellan $date1 och $date2',
uk={'між $date1 та $date2', case={'ins','ins'}},
ur='بین $date1 و $date2',
vec='tra el $date1 e el $date2',
vi='giữa $date1 và $date2',
['zh-hans']='在$date1到$date2之间',
['zh-hant']='在$date1到$date2之間',
default='<span style="color:red">$date1 - $date2</span>',
},
['or'] = {
nDates = 2,
ar='$date1 أو $date2',
['be-tarask']='$date1 ці $date2',
bn='$date1 অথবা $date2',
ca='$date1 o $date2',
ckb='$date1 یان $date2',
cs='$date1 nebo $date2',
da='$date1 eller $date2',
de='$date1 oder $date2',
el='$date1 ή $date2',
en='$date1 or $date2',
['en-gb']='$date1 or $date2',
['en-ca']='$date1 or $date2',
eo='$date1 aŭ $date2',
es='$date1 o $date2',
et='$date1 või $date2',
fa='$date1 یا $date2',
fi='$date1 tai $date2',
fr='$date1 ou $date2',
gl='$date1 ou $date2',
he='$date1 או $date2',
hr='$date1 ili $date2',
hsb='$date1 abo $date2',
hu='$date1 vagy $date2',
it='$date1 o $date2',
ja='$date1または$date2',
mk='$date1 или $date2',
ml='$date1 അഥവാ $date2',
nb='$date1 eller $date2',
nn='$date1 eller $date2',
nds='$date1 oder $date2',
nl='$date1 of $date2',
no='$date1 eller $date2',
pl='$date1 lub $date2',
pt='$date1 ou $date2',
qu='$date1 icha $date2',
ro='$date1 sau $date2',
ru='$date1 или $date2',
sk='$date1 alebo $date2',
sl='$date1 ali $date2',
sv='$date1 eller $date2',
uk='$date1 або $date2',
ur='$date1 یا $date2',
th='$date1 หรือ $date2',
vi='$date1 hoặc $date2',
['zh-hans']='$date1或$date2',
['zh-hant']='$date1或$date2',
default=function(date1, date2, state) return require("Module:linguistic").conj({date1, date2}, state.lang, 'or') end,
},
['and'] = {
nDates = 2,
ar='$date1 و $date2',
['be-tarask']='$date1 і $date2',
bn='$date1 এবং $date2',
ca='$date1 i $date2',
ckb='$date1 و $date2',
cs='$date1 a $date2',
da='$date1 og $date2',
de='$date1 und $date2',
el='$date1 και $date2',
en='$date1 and $date2',
['en-gb']='$date1 and $date2',
['en-ca']='$date1 and $date2',
eo='$date1 kaj $date2',
es='$date1 y $date2',
et='$date1 ja $date2',
fa='$date1 و $date2',
fi='$date1 ja $date2',
fr='$date1 et $date2',
gl='$date1 e $date2',
he='$date1 ו- $date2',
hu='$date1 és $date2',
hsb='$date1 a $date2',
it='$date1 e $date2',
ja='$date1および$date2',
mk='$date1 и $date2',
ml='$date1 ഒപ്പം $date2',
nb='$date1 og $date2',
nn='$date1 og $date2',
nds='$date1 un $date2',
nl='$date1 en $date2',
no='$date1 og $date2',
pl='$date1 i $date2',
pt='$date1 e $date2',
qu='$date1wan $date2pas',
ro='$date1 și $date2',
ru='$date1 и $date2',
sk='$date1 a $date2',
sl='$date1 in $date2',
sv='$date1 och $date2',
uk='$date1 та $date2',
ur='$date1 و $date2',
vi='$date1 và $date2',
['zh-hans']='$date1和$date2',
['zh-hant']='$date1和$date2',
default=function(date1, date2, state) return require("Module:linguistic").conj({date1, date2}, state.lang, 'and') end,
},
-- ==========================================
-- === Prepositions =========================
-- ==========================================
['by'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'да $date1', case={'gen',''}},
bn='$date1 কর্তৃক',
ca='vers $date1',
ckb='نزیکی $date1',
cs={'kolem $date1', case={'gen',''}},
da='mod $date1',
de='spätestens $date1',
el='έως $date1',
en='by $date1',
['en-gb']='by $date1',
['en-ca']='by $date1',
eo='ne post $date1',
es='para $date1',
et='hiljemalt $date1',
fa='توسط $date1',
fi={function(date1, state) return decYear(state,'fi','ill') .. '$date1 mennessä' end, case={'ill',''}},
fr='$date1 (au plus tard)',
gl='cara a $date1',
he='עד $date1',
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='entro il $date1',
ja='$date1まで',
mk='кон $date1',
ml='$date1-ൽ',
nb='innen $date1',
nn='innan $date1',
nds='$date1 oder vördem al',
nl='tegen $date1',
no='innen $date1',
pl={'przed $date1', case={'ins',''}},
pt='por $date1',
qu="$date1 p'unchawkama",
ro='de $date1',
ru={'к $date1', case={'dat',''}},
sk={'okolo $date1', case={'gen',''}},
sl={function(date1, state) return 'najpozneje ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='senast $date1',
uk={'до $date1', case={'dat',''}},
ur='توسط $date1',
vi='vào $date1', -- need to insert "đã" somewhere later in the sentence -->
['zh-hans']='$date1前后',
['zh-hant']='$date1前后',
default='<span style="color:red">by $date1</span>',
},
['before'] = {
nDates = 1,
ar='قبل $date1',
['be-tarask']={'перад $date1', case={'ins',''}},
bn='$date1-এর আগে',
bg='Преди $date1',
ca='abans $date1',
ckb='پێش $date1',
cs={'před $date1', case={'ins',''}},
da='før $date1',
de='vor $date1',
el='πριν από $date1',
en='before $date1',
['en-gb']='before $date1',
['en-ca']='before $date1',
eo='antaŭ $date1',
es='antes de $date1',
et='enne $date1',
fa='پیش از $date1',
fi={function(date1, state) return 'ennen ' .. decYear(state,'fi','ptv') .. '$date1' end, case={'ptv',''}},
fr='avant $date1',
gl='antes do $date1',
he='לפני $date1',
hr='prije $date1',
hsb={'do $date1', case={'gen',''}},
hu='$date1 előtt',
it='prima del $date1',
ja='$date1より前',
mk='пред $date1',
ml='$date1-നു മുമ്പ്',
nb='før $date1',
nn='før $date1',
nds='vör $date1',
nl='voor $date1',
no='før $date1',
pl={'przed $date1', case={'ins',''}},
pt='antes de $date1',
qu="$date1 p'unchaw ñawpan",
ro='înainte de $date1',
ru={'ранее $date1', case={'gen',''}},
sk={'pred $date1', case={'ins',''}},
sl={function(date1, state) return 'pred ' .. decYear(state,'sl','ins') .. ' $date1' end, case={'ins',''}},
sv='före $date1',
uk={'до $date1', case={'gen',''}},
ur='قبل $date1',
vi='trước $date1',
['zh-hans']='在$date1之前',
['zh-hant']='在$date1之前',
default='<span style="color:red">before $date1</span>',
},
['from'] = {
nDates = 1,
ar='من $date1',
['be-tarask']={'з $date1', case={'gen',''}},
bn='$date1 থেকে',
ckb='لە $date1',
cs={'od $date1', case={'gen',''}},
de='Seit $date1',
el='από $date1',
en='from $date1',
['en-gb']='from $date1',
['en-ca']='from $date1',
eo='ekde $date1',
es='desde $date1',
et='alates $date1',
fa='از $date1',
fi={function(date1, state) return decYear(state,'fi','ela') .. '$date1 alkaen' end, case={'ela',''}},
fr= function(date1, state) return 'à partir '.. frOf[state.precision] ..' $date1' end,
gl='desde o $date1',
he='מ- $date1',
hr='od $date1',
hsb={'wot $date1', case={'gen',''}},
it='dal $date1',
ja='$date1から',
nds='vonaf $date1',
mk='од $date1',
nb='fra $date1',
nn='frå $date1',
ml='$date1 മുതൽ',
nl='vanaf $date1',
no='fra $date1',
pl={'od $date1', case={'gen',''}},
qu="$date1 p'unchawmanta",
ro='din $date1',
ru={'С $date1', case={'gen',''}},
sk={'od $date1', case={'gen',''}},
sl={function(date1, state) return 'od ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='från $date1',
uk={'з $date1', case={'gen',''}},
ur='از $date1',
vi='từ $date1',
['zh-hans']='自$date1',
['zh-hant']='自$date1',
default='<span style="color:red">from $date1</span>',
},
['until'] = {
nDates = 1,
['be-tarask']={'усутыч да $date1', case={'gen',''}},
bn='$date1 পর্যন্ত',
ckb='تاوەکوو $date1',
cs={'do $date1', case={'gen', ''}},
de='bis $date1',
en='until $date1',
['en-gb']='until $date1',
['en-ca']='until $date1',
eo='ĝis $date1',
es='hasta $date1',
fr=function(date1, state)
if state.precision > 10 then -- precision > month
return "jusqu'au $date1"
else
return "jusqu'à $date1"
end
end,
hr='do $date1',
hsb={'do $date1', case={'gen',''}},
it='al $date1',
nb='til $date1',
nl='tot $date1',
nn='til $date1',
ml='$date1 വരെ',
pl={'do $date1', case={'gen',''}},
ru={'вплоть до $date1', case={'gen',''}},
sk={'do $date1', case={'gen', ''}},
uk={'аж до $date1', case={'gen',''}},
ur='تک $date1',
['zh-hans']='直到$date1',
['zh-hant']='直到$date1',
default='<span style="color:red">until $date1</span>',
},
['after'] = {
nDates = 1,
ar='بعد $date1',
['be-tarask']={'пасьля $date1', case={'gen',''}},
bn='$date1-এর পরে',
ca='després de $date1',
ckb='دوای $date1',
cs={'po $date1', case={'loc',''}},
de='nach $date1',
da='efter $date1',
el='μετά από $date1',
en='after $date1',
['en-gb']='after $date1',
['en-ca']='after $date1',
eo='post $date1',
es='después de $date1',
et='pärast $date1',
fa='پس از $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 jälkeen' end, case={'gen',''}},
fr='après $date1',
gl='despois do $date1',
he='לאחר $date1',
hr='nakon $date1',
hu='$date1 után',
hsb={'po $date1', case={'loc',''}},
it='dopo il $date1',
ja='$date1より後',
mk='по $date1',
ml='$date1-നു ശേഷം',
nb='etter $date1',
nn='etter $date1',
nds='na $date1',
nl='na $date1',
no='etter $date1',
pl={'po $date1', case={'loc',''}},
pt='após $date1',
qu="$date1 p'unchaw qhipan",
ro='după $date1',
ru={'после $date1', case={'gen',''}},
sk={'po $date1', case={'loc',''}},
sl={function(date1, state) return 'po ' .. decYear(state,'sl','loc') .. ' $date1' end, case={'loc',''}},
sv='efter $date1',
uk={'після $date1', case={'gen',''}},
ur='بعد $date1',
vi='sau $date1',
['zh-hans']='在$date1之后',
['zh-hant']='在$date1之後',
default='<span style="color:red">after $date1</span>',
},
-- ==========================================
-- === Adjectives ===========================
-- ==========================================
['early'] = {
nDates = 1,
ar='بداية $date1',
['be-tarask']={'пачатак $date1', case={'gen',''}},
bn='$date1-এর শুরুতে',
ca='al principi de $date1',
ckb='سەرەتای $date1',
cs={'začátek $date1', case={'gen',''}},
da='tidlig $date1',
de='Anfang $date1',
el='αρχές του $date1',
en='early $date1',
['en-gb']='early $date1',
['en-ca']='early $date1',
eo='komence de $date1',
es='a principios de $date1',
et='$date1 algus',
fa='اوایل $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alku' end, case={'gen',''}},
fr='début $date1',
gl='a principios de $date1',
he='תחילת $date1',
hr='početak $date1',
hsb={'spočatk $date1', case={'gen',''}},
hu='$date1 elején',
it='inizio del $date1',
ja='$date1初頭',
mk='почеток на $date1',
ml='$date1 ആദ്യപാദം',
nb='tidlig $date1',
nn='tidleg $date1',
nds='Anfang $date1',
nl='begin $date1',
no='tidlig $date1',
pl={'początek $date1', case={'gen',''}},
pt='a princípios de $date1',
qu="$date1 ñawpaq p'unchawkunapi",
ro='începând cu $date1',
ru={'начало $date1', case={'gen',''}},
sk={'začiatok $date1', case={'gen',''}},
sl=function(date1, state)-- could actually be better should be "d'octobre à mars 2013' not "de octore 2013 à mars 2013"
if state.precision == 9 then -- precision > month
return 'zgodnje leto $date1'
else -- "DE septebmbre" / "D'octobre"
return 'zgodnji $date1'
end
end,
sv='början av $date1',
uk={'початок $date1', case={'gen',''}},
ur='ابتدا $date1',
vi='đầu năm $date1',
['zh-hans']='$date1早期',
['zh-hant']='$date1早期',
default='<span style="color:red">early $date1</span>',
},
['mid'] = {
nDates = 1,
ar='منتصف $date1',
['be-tarask']={'сярэдзіна $date1', case={'gen',''}},
bn='$date1-এর মাঝামাঝি',
ca='a mitjan $date1',
ckb='ناوەڕاستی $date1',
cs={'polovina $date1', case={'gen',''}},
da='midten af $date1',
de='Mitte $date1',
el='μέσα του $date1',
en='mid $date1',
['en-gb']='mid $date1',
['en-ca']='mid $date1',
eo='meze de $date1',
es='a mediados de $date1',
et='$date1 keskel',
fa='اواسط $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 puoliväli' end, case={'gen',''}},
fr='mi $date1',
gl='a mediados de $date1',
he='אמצע $date1',
hr='sredina $date1',
hsb={'srjedź $date1', case={'gen',''}},
hu='$date1 közepén',
it='metà del $date1',
ja='$date1半ば',
mk='средина на $date1',
ml='$date1 മദ്ധ്യം',
nb='midten av $date1',
nn='midten av $date1',
nds='Midd $date1',
nl='midden $date1',
no='midten av $date1',
pl={'środek $date1', case={'gen',''}},
pt='meados de $date1',
qu="$date1 chawpin p'unchawkunapi",
ro='mijlocul lui $date1',
ru={'середина $date1', case={'gen',''}},
sk={'polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'sredina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='mitten av $date1',
uk={'середина $date1', case={'gen',''}},
ur='وسط $date1',
vi='giữa năm $date1',
['zh-hans']='$date1中期',
['zh-hant']='$date1中期',
default='<span style="color:red">mid $date1</span>',
},
['late'] = {
nDates = 1,
ar='نهاية $date1',
bn='$date1-এর শেষে',
['be-tarask']={'канец $date1', case={'gen',''}},
ca='al final de $date1',
ckb='کۆتاییی $date1',
cs='konec $date1',
da='sen $date1',
de='Ende $date1',
el='τέλη του $date1',
en='late $date1',
['en-gb']='late $date1',
['en-ca']='late $date1',
eo='fine de $date1',
es='a fines de $date1',
et='$date1 lõpp',
fa='اواخر $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppu' end, case={'gen',''}},
fr='fin $date1',
gl='a finais de $date1',
he='סוף $date1',
hr='kraj $date1',
hsb={'kónc $date1', case={'gen',''}},
hu='$date1 végén',
it='fine del $date1',
ja='$date1末',
mk='крајот на $date1',
ml='$date1 അന്ത്യപാദം',
nb='sent $date1',
no='seint $date1',
nds='Enn $date1',
nl='eind $date1',
no='sent $date1',
pl={'koniec $date1', case={'gen',''}},
pt='ao final de $date1',
qu="$date1 qhipaq p'unchawkunapi",
ro='sfârșitul lui $date1',
ru={'конец $date1', case={'gen',''}},
sk={'koniec $date1', case={'gen',''}},
sl={function(date1, state) return 'pozno ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='slutet av $date1',
uk={'кінець $date1', case={'gen',''}},
ur='اخیر $date1',
vi='cuối năm $date1',
['zh-hans']='$date1晚期',
['zh-hant']='$date1晚期',
default='<span style="color:red">late $date1</span>',
},
['circa'] = {
nDates = 1,
ar='حوالي $date1',
['be-tarask']={'каля $date1', case={'gen',''}},
bn='প্রায় $date1',
ckb='بەنزیکی $date1',
ca='cap a $date1',
cs='cca $date1',
da='ca. $date1',
de='etwa $date1',
el='περ. $date1',
en='c. $date1',
['en-gb']='circa $date1',
['en-ca']='circa $date1',
eo='ĉirkaŭ $date1',
es='hacia $date1', -- suggestion use "hacia"/"hacia el" or leave the latin circa or ca.
et='ca. $date1',
fa='حوالی $date1', -- template:Circa used 'حدود $date1'
fi={function(date1, state) return 'suunnilleen ' .. decYear(state,'fi','ine') .. '$date1' end, case={'ine',''}}, -- template:Circa used 'noin $date1'
fr=function(date1, state)
if state.precision >= 11 then -- precision >= day
return 'vers le $date1'
else
return 'vers $date1'
end
end,
gl='entre $date1', -- template:Circa used 'cara a $date1'
he='בסביבות $date1',
hr='oko $date1',
hsb={'wokoło $date1', case={'gen',''}},
hu='$date1 körül',
it='$date1 circa',
ja='$date1頃',
ko='$date1경',
mk='~ $date1', -- template:Circa used 'o. $date1'
ml= 'ഉദ്ദേശം $date1',
nb='ca. $date1',
nn='ca. $date1',
nds='üm un bi $date1',
nl='ca. $date1',
no='ca. $date1',
pl={'około $date1', case={'gen',''}},
pt='cerca de $date1',
qu='$date1chá',
ro='circa $date1',
ru={'около $date1', case={'gen',''}},
sk='cca $date1',
sl={function(date1, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='c:a $date1', -- template:Circa used 'ca $date1'
uk={'близько $date1', case={'gen', ''}},
ur='قریباً $date1',
vi='vào khoảng $date1',
['zh-hans']='约$date1',
['zh-hant']='約$date1',
default='<span style="color:red">circa $date1</span>',
},
['uncertain'] = {
nDates = 1,
['zh-hans']='$date1(?)',
['zh-hant']='$date1(?)',
default='$date1 (?)',
},
probably = { -- see Q56644435
nDates = 1,
ast = 'probablemente $date1',
bn = 'সম্ভবত $date1',
ca = 'probablement $date1',
ckb = 'لەوانەیە $date1',
cs = 'pravděpodobně $date1',
de = 'wahrscheinlich $date1',
el = 'πιθανόν $date1',
en = 'probably $date1',
['en-gb']='probably $date1',
['en-ca']='probably $date1',
eo = 'probable $date1',
es = 'probable $date1',
fa = 'احتمالاً $date1',
fr = 'probablement $date1',
he = 'כפי הנראה $date1',
hu = 'feltehetőleg $date1',
it = 'probabile $date1',
mk = 'веројатно $date1',
ml = 'ഒരുപക്ഷേ $date1',
nl = 'waarschijnlijk $date1',
pl = 'prawdopodobnie $date1',
pt = 'provavelmente $date1',
ru = 'вероятно $date1',
sv = 'antagligen $date1',
uk = 'ймовірно $date1',
default='<span style="color:red">probably $date1</span>',
},
possibly = { -- see Q30230067
nDates = 1,
ast = 'posiblemente $date1',
bn = 'যথাসম্ভব $date1',
ckb = 'لەوانەیە $date1',
cs = 'možná $date1',
de = 'möglicherweise $date1',
en = 'possibly $date1',
['en-gb']='possibly $date1',
['en-ca']='possibly $date1',
eo = 'eble $date1',
el = 'πιθανόν $date1',
fa = 'احتمالاً $date1',
fr = 'peut-être $date1',
hu = 'talán $date1',
mk = 'можеби $date1',
ml = 'മിക്കവാറും $date1',
nl = 'mogelijk $date1',
pl = 'być może $date1',
pt = 'possivelmente $date1',
ro = 'poate $date1',
ru = 'возможно $date1',
sv = 'möjligen $date1',
uk = 'можливо $date1',
default='<span style="color:red">possibly $date1</span>',
},
presumably = { -- see Q18122778
nDates = 1,
ast = 'presumiblemente $date1',
ckb = 'دەشێت $date1',
de = 'vermutlich $date1',
en = 'presumably $date1',
['en-gb']='presumably $date1',
['en-ca']='presumably $date1',
fr = 'présumé $date1',
it = 'presumibilmente $date1',
ml = 'ഊഹപ്രകാരം $date1',
ru = 'предположительно $date1',
pl = 'przypuszczalnie $date1',
uk = 'здогадно $date1',
default='<span style="color:red">presumably $date1</span>',
},
['year unknown']= {
nDates = 0,
ca='Any desconegut',
['be-tarask']='Год невядомы',
bn='অজানা বছর',
ckb='ساڵی نەزاندراو',
cs='neznámý rok',
de='Jahr unbekannt',
el='άγνωστο έτος',
en='year unknown',
['en-gb']='year unknown',
['en-ca']='year unknown',
eo='jaro nesciata',
es='Año desconocido',
fa='سال نامعلوم',
fi='tuntematon vuosi',
fr='Année inconnue',
he='שנה לא ידועה',
hsb='lěto njeznate',
hr='nepoznata godina',
it='anno sconosciuto',
ja='年代不明',
la='Sine anno',
ml='വർഷം ലഭ്യമല്ല',
mk='непозната година',
nb='ukjent år',
nn='ukjent år',
nl='zonder jaar',
no='ukjent år',
pl='rok nieznany',
qu='Mana riqsisqa wata',
ru='Год неизвестен',
sk='neznámy rok',
sl='neznano leto',
sv='Okänt år',
uk='рік невідомий',
ur='سال نامعلوم',
vi='không rõ năm',
['zh-hans']='年份未知',
['zh-hant']='年份未知',
default='<span style="color:red">year unknown</span>',
},
-- ==========================================
-- === Parts of a whole =====================
-- ==========================================
['firsthalf'] = {
nDates = 1,
ar='النصف الأول من $date1',
['be-tarask']={'першая палова $date1', case={'gen',''}},
bn='$date1-এর প্রথমার্ধ',
ca='1a meitat de $date1',
ckb='نیوەی یەکەمی $date1',
cs={'1. polovina $date1', case={'gen',''}},
da='1. halvdel af $date1',
de='1. Hälfte $date1',
el='πρώτο μισό του $date1',
en='first half of $date1',
['en-gb']='first half of $date1',
['en-ca']='first half of $date1',
eo='unua duono de $date1',
es='primera mitad de $date1',
et='$date1 esimene pool',
fa='نیمهٔ نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 alkupuolisko' end, case={'gen',''}},
fr='1<sup>re</sup> moitié de $date1',
gl='primeira metade de $date1',
he='במחצית הראשונה של $date1',
hsb={'prěnja połojca $date1', case={'gen',''}},
hu='$date1 első felében',
hr='prva polovina $date1',
it='prima metà del $date1',
ja='$date1上半期',
mk='прва половина на $date1',
ml='$date1 ആദ്യപകുതി',
nb='første halvdel av $date1',
nn='fyrste halvdelen av $date1',
nds='1. Helft $date1',
nl='eerste helft $date1',
no='første halvdel av $date1',
pl={'pierwsza połowa $date1', case={'gen',''}},
pt='primeira metade de $date1',
qu='$date1 ñawpaq kuskanpi',
ro='prima jumătate a lui $date1',
ru={'первая половина $date1', case={'gen',''}},
sk={'1. polovica $date1', case={'gen',''}},
sl={function(date1, state) return 'prva polovica ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första halvan av $date1',
uk={'перша половина $date1', case={'gen',''}},
ur='نصف اول $date1',
vi='nửa đầu năm $date1',
['zh-hans']='$date1上半期',
['zh-hant']='$date1上半期',
default='<span style="color:red">first half of $date1</span>',
},
['secondhalf'] = {
nDates = 1,
ar='النصف الثاني من $date1',
['be-tarask']={'другая палова $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয়ার্ধ',
ca='2a meitat de $date1',
ckb='نیوەی دووەمی $date1',
cs={'2. polovina $date1', case={'gen',''}},
da='2. halvdel af $date1',
de='2. Hälfte $date1',
el='δεύτερο μισό του $date1',
en='second half of $date1',
['en-gb']='second half of $date1',
['en-ca']='second half of $date1',
eo='dua duono de $date1',
es='segunda mitad de $date1',
et='$date1 teine pool',
fa='نیمهٔ دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 loppupuolisko' end, case={'gen',''}},
fr='2<sup>e</sup> moitié de $date1',
gl='segunda metade de $date1',
he='במחצית השנייה של $date1',
hsb={'druha połojca $date1', case={'gen',''}},
hr='druga polovina $date1',
hu='$date1 második felében',
it='seconda metà del $date1',
ja='$date1下半期',
mk='втора половина на $date1',
ml='$date1 രണ്ടാം പകുതി',
nb='andre halvdel av $date1',
nn='andre halvdelen av $date1',
nds='2. Helft vun $date1',
nl='tweede helft $date1',
no='andre halvdel av $date1',
pl={'druga połowa $date1', case={'gen',''}},
pt='segunda metade de $date1',
qu='$date1 qhipaq kuskanpi',
ro='a doua jumătate a lui $date1',
ru={'вторая половина $date1', case={'gen',''}},
sk={'2. polovica $date1', case={'gen',''}},
sl={'druga polovica {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sv='andra halvan av $date1',
uk={'друга половина $date1', case={'gen',''}},
ur='نصف آخر $date1',
vi='nửa cuối năm $date1',
['zh-hans']='$date1下半期',
['zh-hant']='$date1下半期',
default='<span style="color:red">second half of $date1</span>',
},
['1quarter']= {
nDates = 1,
bn='$date1-এর প্রথম ত্রৈমাসিক',
['be-tarask']={'першая чвэрць $date1', case={'gen',''}},
ckb='چارەکی یەکەمی $date1',
cs={'1. čtvrtina $date1', case={'gen',''}},
de='erstes Viertel $date1',
en='first quarter of $date1',
['en-gb']='first quarter of $date1',
['en-ca']='first quarter of $date1',
eo='unua kvarono de $date1',
es='primer cuarto del $date1',
fa='ربع نخست $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 ensimmäinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'premier quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון ראשון של $date1',
hsb={'prěnja štwórćina $date1', case={'gen',''}},
hr='prva četvrtina $date1',
it='primo quarto del $date1',
ja='$date1第1四半期',
ml='$date1 ഒന്നാം ത്രൈമാസം',
nb='første kvartal av $date1',
nl='eerste kwart $date1',
nn='fyrste kvartalet av $date1',
no='første kvartal av $date1',
pl={'pierwsza ćwiartka $date1', case={'gen',''}},
qu='$date1 ñawpaq ñiqin tawa phatmanpi',
ru={'первая четверть $date1', case={'gen',''}},
sk={'1. štvrtina $date1', case={'gen',''}},
sl={'prva četrtina {{IfNum | $date0 | leta}} $date1', case={'gen',''}},
sl={function(date1, state) return 'prva četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='första kvartalet av $date1',
uk={'1-ша чверть $date1', case={'gen',''}},
ur='پہلی سہ ماہی $date1',
vi='quý 1 năm $date1',
['zh-hans']='$date1第一季度',
['zh-hant']='$date1第一季度',
default='<span style="color:red">first quarter of $date1</span>',
},
['2quarter']= {
nDates = 1,
['be-tarask']={'другая чвэрць $date1', case={'gen',''}},
bn='$date1-এর দ্বিতীয় ত্রৈমাসিক',
ckb='چارەکی دووەمی $date1',
cs={'2. čtvrtina $date1', case={'gen',''}},
de='zweites Viertel $date1',
en='second quarter of $date1',
['en-gb']='second quarter of $date1',
['en-ca']='second quarter of $date1',
eo='dua kvarono de $date1',
es='segundo cuarto del $date1',
fa='ربع دوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 toinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'deuxième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שני של $date1',
hsb={'druha štwórćina $date1', case={'gen',''}},
hr='druga četvrtina $date1',
it='secondo quarto del $date1',
ja='$date1第2四半期',
ml='$date1 രണ്ടാം ത്രൈമാസം',
nb='andre kvartal av $date1',
nl='tweede kwart $date1',
nn='andre kvartalet av $date1',
no='andre kvartal av $date1',
pl={'druga ćwiartka $date1', case={'gen',''}},
qu='$date1 iskay ñiqin tawa phatmanpi',
ru={'вторая четверть $date1', case={'gen',''}},
sk={'2. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'druga četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='andra kvartalet av $date1',
uk={'2-га чверть $date1', case={'gen',''}},
ur='دوسری سہ ماہی $date1',
vi='quý 2 năm $date1',
['zh-hans']='$date1第二季度',
['zh-hant']='$date1第二季度',
default='<span style="color:red">second quarter of $date1</span>',
},
['3quarter']= {
nDates = 1,
['be-tarask']={'трэцяя чвэрць $date1', case={'gen',''}},
bn='$date1-এর তৃতীয় ত্রৈমাসিক',
ckb='چارەکی سێیەمی $date1',
cs={'3. čtvrtina $date1', case={'gen',''}},
de='drittes Viertel $date1',
en='third quarter of $date1',
['en-gb']='third quarter of $date1',
['en-ca']='third quarter of $date1',
eo='tria kvarono de $date1',
es='tercer cuarto del $date1',
fa='ربع سوم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 kolmas neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'troisième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון שלישי של $date1',
hsb={'třeća štwórćina $date1', case={'gen',''}},
hr='treća četvrtina $date1',
it='terzo quarto del $date1',
ja='$date1第3四半期',
ml='$date1 മൂന്നാം ത്രൈമാസം',
nb='tredje kvartal av $date1',
nl='derde kwart $date1',
nn='tredje kvartalet av $date1',
no='tredje kvartal av $date1',
pl={'trzecia ćwiartka $date1', case={'gen',''}},
qu='$date1 kimsa ñiqin tawa phatmanpi',
sk={'3. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'tretja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
ru={'третья четверть $date1', case={'gen',''}},
sv='tredje kvartalet av $date1',
uk={'3-тя чверть $date1', case={'gen',''}},
ur='تیسری سہ ماہی $date1',
vi='quý 3 năm $date1',
['zh-hans']='$date1第三季度',
['zh-hant']='$date1第三季度',
default='<span style="color:red">third quarter of $date1</span>',
},
['4quarter']= {
nDates = 1,
['be-tarask']={'чацьвёртая чвэрць $date1', case={'gen',''}},
bn='$date1-এর চতুর্থ ত্রৈমাসিক',
ckb='چارەکی چوارەمی $date1',
cs={'4. čtvrtina $date1', case={'gen',''}},
de='letztes Viertel $date1',
en='fourth quarter of $date1',
['en-gb']='fourth quarter of $date1',
['en-ca']='fourth quarter of $date1',
eo='kvara kvarono de $date1',
es='último cuarto del $date1',
fa='ربع چهارم $date1',
fi={function(date1, state) return decYear(state,'fi','gen') .. '$date1 viimeinen neljännes' end, case={'gen',''}},
fr= function(date1, state) return 'quatrième quart '.. frOf[state.precision] ..' $date1' end,
he='רבעון רביעי של $date1',
hsb={'poslednja štwórćina $date1', case={'gen',''}},
hr='četvrta četvrtina $date1',
it='quarto quarto del $date1',
ja='$date1第4四半期',
ml='$date1 നാലാം ത്രൈമാസം',
nb='fjerde kvartal av $date1',
nl='vierde kwart $date1',
nn='tredje kvartalet av $date1',
no='fjerde kvartal av $date1',
pl={'czwarta ćwiartka $date1', case={'gen',''}},
qu='$date1 qhipaq ñiqin tawa phatmanpi',
ru={'последняя четверть $date1', case={'gen',''}},
sk={'4. štvrtina $date1', case={'gen',''}},
sl={function(date1, state) return 'zadnja četrtina ' .. decYear(state,'sl','gen') .. ' $date1' end, case={'gen',''}},
sv='fjärde kvartalet av $date1',
uk={'4-та чверть $date1', case={'gen',''}},
ur='چوتھی سہ ماہی $date1',
vi='quý 4 năm $date1',
['zh-hans']='$date1第四季度',
['zh-hant']='$date1第四季度',
default='<span style="color:red">forth quarter of $date1</span>',
},
-- ==========================================
-- === Seasons ==============================
-- ==========================================
['spring'] = {
nDates = 1,
ar='ربيع $date1',
['be-tarask']='вясна $date1',
bn='বসন্তকাল $date1',
ca='primavera $date1',
ckb='بەھاری $date1',
cs={'jaro $date1', case={'gen', ''}},
da='Forår $date1',
de='Frühling $date1',
en='Spring $date1',
['en-gb']='Spring $date1',
['en-ca']='Spring $date1',
eo='printempo $date1',
es='primavera de $date1',
et='$date1 kevad',
fa='بهار $date1',
fi='Kevät $date1',
fr='printemps $date1',
gl='primavera $date1',
he='אביב $date1',
hr='proljeće $date1',
hsb='nalěćo $date1',
hu='$date1 tavasza',
it='primavera $date1',
ja='{{#if:$date1|$date1年}}春',
mk='пролет $date1',
ml='$date1 വസന്തം',
nb='våren $date1',
nds='Vörjohr $date1',
nn='våren $date1',
nl='voorjaar $date1',
no='våren $date1',
pl='wiosna $date1',
pt='primavera de $date1',
ro='primăvara lui $date1',
ru='весна $date1',
sk={'jar $date1', case={'gen', ''}},
sl='pomlad $date1',
sv='våren $date1',
uk='весна $date1',
ur='بہار $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}春季',
['zh-hant']='{{#if:$date1|$date1年}}春季',
default='<span style="color:red">Spring $date1</span>',
},
['summer'] = {
nDates = 1,
ar='صيف $date1',
['be-tarask']='лета $date1',
bn='গ্রীষ্মকাল $date1',
ca='estiu $date1',
ckb='ھاوینی $date1',
cs={'léto $date1', case={'gen', ''}},
da='Sommer $date1',
de='Sommer $date1',
en='Summer $date1',
['en-gb']='Summer $date1',
['en-ca']='Summer $date1',
eo='somero $date1',
es='verano de $date1',
et='$date1 suvi',
fa='تابستان $date1',
fi='Kesä $date1',
fr='été $date1',
gl='verán $date1',
he='קיץ $date1',
hr='ljeto $date1',
hsb='lěćo $date1',
hu='$date1 nyara',
it='estate $date1',
ja='{{#if:$date1|$date1年}}夏',
mk='лето $date1',
ml='$date1 വേനൽ',
nb='sommeren $date1',
nds='Sommer $date1',
nn='sumaren $date1',
nl='zomer $date1',
no='sommeren $date1',
pl='lato $date1',
pt='verão de $date1',
ro='vara lui $date1',
ru='лето $date1',
sk={'leto $date1', case={'gen', ''}},
sl='poletje $date1',
sv='sommaren $date1',
uk='літо $date1',
ur='گرما $date1',
vi='mùa hè năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}夏季',
['zh-hant']='{{#if:$date1|$date1年}}夏季',
default='<span style="color:red">Summer $date1</span>',
},
['autumn'] = {
nDates = 1,
ar='خريف $date1',
['be-tarask']='восень $date1',
bn='শরত্কাল $date1',
ca='tardor $date1',
ckb='پاییزی $date1',
cs={'podzim $date1', case={'gen', ''}},
da='Efterår $date1',
de='Herbst $date1',
en='Autumn $date1',
['en-gb']='Autumn $date1',
['en-ca']='Autumn $date1',
eo='aŭtuno $date1',
es='otoño de $date1',
et='$date1 sügis',
fa='پاییز $date1',
fi='Syksy $date1',
fr='automne $date1',
gl='outono $date1',
he='סתיו $date1',
hr='jesen $date1',
hsb='nazyma $date1',
hu='$date1 ősze',
it='autunno $date1',
ja='{{#if:$date1|$date1年}}秋',
mk='есен $date1',
ml='$date1 ശരത്കാലം',
nb='høsten $date1',
nds='Harvst $date1',
nn='hausten $date1',
nl='herfst $date1',
no='høsten $date1',
pl='jesień $date1',
pt='outono de $date1',
ro='toamna lui $date1',
ru='осень $date1',
sk={'jeseň $date1', case={'gen', ''}},
sl='jesen $date1',
sv='hösten $date1',
uk='осінь $date1',
ur='خزاں $date1',
vi='mùa thu năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}秋季',
['zh-hant']='{{#if:$date1|$date1年}}秋季',
default='<span style="color:red">Autumn $date1</span>',
},
['winter'] = {
nDates = 1,
ar='شتاء $date1',
['be-tarask']='зіма $date1',
bn='শীতকাল $date1',
ca='hivern $date1',
ckb='زستانی $date1',
cs={'zima $date1', case={'gen', ''}},
da='Vinter $date1',
de='Winter $date1',
en='Winter $date1',
['en-gb']='Winter $date1',
['en-ca']='Winter $date1',
eo='vintro $date1',
es='invierno de $date1',
et='$date1 talv',
fa='زمستان $date1',
fi='Talvi $date1',
fr='hiver $date1',
gl='inverno $date1',
he='חורף $date1',
hr='zima $date1',
hsb='zyma $date1',
hu='$date1 tele',
it='inverno $date1',
ja='{{#if:$date1|$date1年}}冬',
mk='зима $date1',
ml='$date1 ശൈത്യകാലം',
nds='Winter $date1',
nl='winter $date1',
nb='vinteren $date1',
nn='vinteren $date1',
no='vinteren $date1',
pl='zima $date1',
pt='inverno de $date1',
ro='iarna lui $date1',
ru='зима $date1',
sk={'zima $date1', case={'gen', ''}},
sl='zima $date1',
sv='vintern $date1',
uk='зима $date1',
ur='سرما $date1',
vi='mùa đông năm $date1',
['zh-hans']='{{#if:$date1|$date1年}}冬季',
['zh-hant']='{{#if:$date1|$date1年}}冬季',
default='<span style="color:red">Winter $date1</span>',
},
-- ==========================================
-- === Decade/Century/Millennium ============
-- ==========================================
['decade'] = {
nDates = 1,
ar='أعوام $date1',
['be-tarask']='$date1-я гады',
bn='$date1-এর দশক',
ca='dècada del $date1',
ckb='دەیەی $date1',
cs=function(date1, state) return ((date1 % 100 == 0) and 'první dekáda' or ((date1 % 100) .. '. léta')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. století' end,
da="$date1'erne",
de='$date1er-Jahre',
el='δεκαετία του $date1',
en='$date1s',
['en-gb']='$date1s',
['en-ca']='$date1s',
eo='$date1-aj jaroj',
es='años $date1',
et='$date1ndad',
fa='دههٔ $date1',
fi='$date1-luvulla',
fr='années $date1',
gl='anos $date1',
he='שנות ה-$date1',
hr='$date1-ih',
hsb='$date1te lěta',
hu='$date1-s évek',
it="{{#ifexpr: $date1 mod 100 = 0 | primo decennio del {{Roman|{{#expr: ($date1 / 100) + 1}}}} sec. | anni '{{#expr: $date1 mod 100 }} del {{Roman|{{#expr: floor($date1 / 100) + 1}}}} sec.}}",
ja='$date1年代',
mk='$date1-ти',
ml='ദശകം $date1-ൽ',
nb='$date1-årene',
nn='$date1-åra',
nds='Johren $date1',
nl='jaren $date1',
no='$date1-årene',
pl=function(date1, state)
local str = 'lata '..(date1 % 100)..'. '
if (date1 % 100 == 0) then
str = 'pierwsza dekada '
elseif (date1 % 100 == 10) then
str = 'druga dekada '
end
return str .. Roman(math.floor(date1 / 100) + 1) .. ' wieku'
end,
pt='década de $date1',
qu='$date1 watakuna',
ro='anii $date1',
ru='$date1-е годы',
sk=function(date1, state) return ((date1 % 100 == 0) and 'prvé desaťročie' or ((date1 % 100) .. '. roky')) .. ' ' .. (math.floor(date1 / 100) + 1) .. '. storočia' end,
sl='$date1-ta leta',
sv='$date1-talet{{#switch:$date1|1300|1400|1500|1600|1700|1800|1900|2000|2100= (decennium)}}',
uk='$date1-ті роки',
ur='دہائی $date1',
vi='thập niên $date1',
['zh-hans']='$date1年代',
['zh-hant']='$date1年代',
default='<span style="color:red">$date1s</span>',
},
['century'] = {
nDates = 1,
ar='القرن $date1',
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' стагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' শতাব্দী' end,
ca='segle {{small-caps|{{lc:{{Roman year|$date1}}}}}}',
ckb='سەدەی $date1',
cs='$date1. století',
da='$date1. århundrede',
de='$date1. Jahrhundert',
el='$date1ος αιώνας',
en=function(date1, state) return Ordinal(date1, 'en') .. ' century' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' century' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' century' end,
eo='$date1-a jarcento',
es=function(date1, state) return 'siglo ' .. Roman(date1) end,
et='$date1. sajand',
fa='سدهٔ $date1',
fi='{{#expr: ($date1 - 1) * 100}}-luvulla',
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle' end,
gl=function(date1, state) return 'século ' .. Roman(date1) end,
he='המאה ה-$date1',
hr='$date1. stoljeće',
hsb='$date1. lětstotk',
hu='$date1. évszázad',
it=function(date1, state) return Roman(date1) .. ' sec.' end,
ja='$date1世紀',
la='saeculum $date1',
mk=function(date1, state) return Roman(date1) .. ' век' end,
ml='$date1-ആം നൂറ്റാണ്ട്',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' århundre' end,
nn=function(date1, state) return 'det ' .. Ordinal(date1, 'no') .. ' århundre' end,
nds='$date1. Johrhunnert',
nl='$date1e eeuw',
no=function(date1, state) return Ordinal(date1, 'no') .. ' århundre' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decCentury('pl',state.case) end,
pt=function(date1, state) return 'século ' .. Roman(date1) end,
qu='$date1 ñiqin pachakwata',
ro='secolul al $date1-lea',
ru=function(date1, state) return Roman(date1) .. ' век' end,
sk='$date1. storočie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. stoletje' end,
sv='{{#expr:$date1 - 1}}00-talet',
tt=function(date1, state) return Roman(date1) .. ' гасыр' end,
uk=function(date1, state) return Roman(date1) .. ' ст.' end,
ur='صدی $date1',
vi='thế kỷ $date1',
['zh-hans']='$date1世纪',
['zh-hant']='$date1世紀',
default='<span style="color:red">$date1 century</span>',
},
['millennium'] = {
nDates = 1,
['be-tarask']=function(date1, state) return Ordinal(date1, 'be-tarask') .. ' тысячагодзьдзе' end,
bn=function(date1, state) return Ordinal(date1, 'bn') .. ' সহস্রাব্দ' end,
ckb=function(date1, state) return 'ھەزارەی ' .. Ordinal(date1, 'ckb') end,
cs='$date1. tisíciletí',
de='$date1. Jahrtausend',
en=function(date1, state) return Ordinal(date1, 'en') .. ' millennium' end,
['en-gb']=function(date1, state) return Ordinal(date1, 'en-gb') .. ' millennium' end,
['en-ca']=function(date1, state) return Ordinal(date1, 'en-ca') .. ' millennium' end,
eo='$date1-a jarmilo',
es=function(date1, state) return Roman(date1) .. ' milenio' end,
fa=function(date1, state) return 'هزارهٔ ' .. Ordinal(date1, 'fa') end,
fr=function(date1, state) return Ordinal(date1, 'fr', 'roman', 'm') .. ' millénaire' end,
hr='$date1. tisućljeće',
hsb='$date1. lěttysac',
it=function(date1, state) return Roman(date1) .. ' millennio' end,
ja='$date1千年紀',
la='millennium $date1',
ml='$date1-മത്തെ സഹസ്രാബ്ദം',
nb=function(date1, state) return Ordinal(date1, 'nb') .. ' årtusen' end,
nn=function(date1, state) return Ordinal(date1, 'no') .. ' tusenår' end,
nl='$date1e millennium',
no=function(date1, state) return Ordinal(date1, 'no') .. ' årtusen' end,
pl=function(date1, state) return Roman(date1) .. ' ' .. decMillennium('pl',state.case) end,
pt=function(date1, state) return Roman(date1) .. ' milénio' end,
qu='$date1 ñiqin waranqawata',
ru=function(date1, state) return Roman(date1) .. ' тысячелетие' end,
sk='$date1. tisícročie',
sl=function(date1, state) return Ordinal(date1, 'sl') .. '. tisočletje' end,
sv=function(date1, state) return Ordinal(date1, 'sv') .. ' årtusendet' end,
uk=function(date1, state) return Roman(date1) .. ' тис.' end,
ur=function(date1, state) return 'ہزارہ ' .. Ordinal(date1, 'ur') end,
vi='thiên niên kỷ $date1',
['zh-hans']=function(date1, state) return Ordinal(date1, 'zh-hans') .. '千年' end,
['zh-hant']=function(date1, state) return Ordinal(date1, 'zh-hant') .. '千年' end,
--default='{{Please translate|$date1 millennium|Module:I18n/complex date}}',
default='<span style="color:red">$date1 millennium</span>',
},
['million'] = {
ckb = '$date1 میلیۆن ساڵ',
de = '$date1 Millionen Jahre',
en = '$date1 million years',
['en-gb'] = '$date1 million years',
['en-ca'] = '$date1 million years',
eo = '$date1 milionoj da jaroj',
es = '$date1 millones de años',
nl = '$date1e miljoen jaar',
fr = '$date1 millions d’années',
it = '$date1 milioni di anni',
ml = function(date1, state)
if date1 >= 10 then
return (date1 / 10) .. ' കോടി വർഷങ്ങൾ' -- 100 lakh = 1 crore
else
return (date1 * 10) .. ' ലക്ഷം വർഷങ്ങൾ'
end
end,
nb = '$date1 millioner år',
nl = '$date1 miljoen jaar',
nn = '$date1 millionar år',
pl = '$date1 milionów lat',
pt = '$date1 milhões de anos',
ru = '$date1 миллиона лет',
uk = '$date1 мільйони років',
ur = '$date1 ملین سال',
['zh-hans'] = '$date1百万年',
['zh-hant'] = '$date1百万年',
default='<span style="color:red">$date1 million years</span>',
nDates = 1,
},
['billion'] = {
ckb = '$date1 بیلیۆن ساڵ',
de = '$date1 Milliarden Jahre',
en = '$date1 billion years',
['en-gb'] = '$date1 billion years',
['en-ca'] = '$date1 billion years',
es = '$date1 mil millones de años',
nl = '$date1e miljard jaar',
fr = '$date1 milliard d’années',
it = '$date1 miliardi di anni',
ml = function(date1, state) return (date1 * 10) .. ' കോടി വർഷങ്ങൾ' end,
nb = '$date1 milliarder år',
nl = '$date1 miljard jaar',
nn = '$date1 milliardar år',
pl = '$date1 miliardów lat',
pt = '$date1 mil milhões de anos',
ru = '$date1 миллиардов лет',
uk = '$date1 мільярдів років',
ur = '$date1 بلین سال',
['zh-hans'] = '$date1十亿年',
['zh-hant'] = '$date1十億年',
default='<span style="color:red$date1 billion years</span>',
nDates = 1,
},
-- ==========================================
-- === Complex phrases ======================
-- ==========================================
-- Phrases which for some languages can be build out of simpler phrases
-- Their "default" state, used when no translation in the requested language was found, returns a "state" like
-- table which is used to call the function again and built the phrase out of simple phrases
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the century'] = {
nDates = 2,
['be-tarask']=function(date1, date2, state) return 'канец ' .. Roman(date1) ..' — пачатак ' .. Roman(date2) ..' стагодзьдзя' end,
bn=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'bn') .. ' শতাব্দীর পালাবদলে' end,
ckb=function(date1, date2, state) return 'سەردەمی $date1/' .. Ordinal(date2, 'ckb') .. ' سەدە' end,
cs='přelom $date1. a $date2. století',
de=function(date1, date2, state) return 'Jahrhundertwende $date1/' .. Ordinal(date2, 'de') end,
en=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en') .. ' century' end,
['en-gb']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-gb') .. ' century' end,
['en-ca']=function(date1, date2, state) return 'turn of the $date1/' .. Ordinal(date2, 'en-ca') .. ' century' end,
eo='ŝanĝo de la $date1-a al la $date2-a jarcentoj',
es=function(date1, date2, state) return 'hacia finales del siglo ' .. Roman(date1) .. ' y principios del siglo ' .. Roman(date2) .. '' end,
fi='{{#expr: $date1 * 100-100}}-luvun loppu ja {{#expr: $date2 * 100-100}}-luvun alku',
fr=function(date1, date2, state) return 'fin du ' .. Ordinal(date1, 'fr', 'roman', 'm') .. ' siècle / début du ' .. Ordinal(date2, 'fr', 'roman', 'm') end,
hr='kraj $date1. i početak $date2. stoljeća',
hsb=function(date1, date2, state) return 'přechod ' .. Roman(date1) ..' do ' .. Roman(date2) ..' lětstotka' end,
it=function(date1, date2, state) return 'fine del ' .. Roman(date1) .. ' sec. e inizio del ' .. Roman(date2) .. ' sec.' end,
ja='$date1世紀から$date2世紀への変わり目',
ml='$date1 നൂറ്റാണ്ട് അവസാനവും $date2 നൂറ്റാണ്ട് ആദ്യവും',
nb=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nb') .. ' århundre/begynnelsen av det ' .. Ordinal(date2, 'nb') .. ' århundre' end,
nl=function(date1, date2, state) return 'eind $date1e/begin $date2e eeuw' end,
nn=function(date1, date2, state) return 'slutten av det ' .. Ordinal(date1, 'nn') .. ' hundreåret/byrjinga av det ' .. Ordinal(date2, 'nn') .. ' hundreåret' end,
pl=function(date1, date2, state) return 'przełom ' .. Roman(date1) ..' i ' .. Roman(date2) ..' wieku' end,
qu="$date1 ñiqin waranqawatamanta $date2 ñiqin waranqawataman t'ikrasqanpi",
ru=function(date1, date2, state) return 'конец ' .. Roman(date1) ..' — начало ' .. Roman(date2) ..' века' end,
sk='prelom $date1. a $date2. storočia',
sl=function(date1, date2, state) return 'pozno ' .. Ordinal(date1, 'sl') .. '. in zgodnje ' .. Ordinal(date2, 'sl') .. '. stoletje' end,
sv=function(date1, date2, state) return '$date1/' .. Ordinal(date2, 'sv') .. ' årtusendets sekelskifte' end,
uk=function(date1, date2, state) return 'кін. ' .. Roman(date1) ..' ст. — поч. ' .. Roman(date2) ..' ст.' end,
vi='bước sang thế kỷ $date2',
['zh-hans']=function(date1, date2, state) return '$date1世纪末' .. Ordinal(date2, 'zh-hans') .. '世纪初' end,
['zh-hant']=function(date1, date2, state) return '$date1世紀末' .. Ordinal(date2, 'zh-hant') .. '世紀初' end,
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='century', era1=state.era1, adj2 = 'early', unit2='century', era2=state.era2} end,
},
-- $date1 - original date - 10
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the decade']={
nDates = 2,
['be-tarask']='канец $date1-х — пачатак $date2-х гг.',
bn='$date1-এর দশকের শেষে এবং $date2-এর দশকের শুরুতে',
cs=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "přelom 1. a 2. dekády " .. century1 .. ". století"
elseif decade2 == 0 and date2 < date1 then
return "přelom 9. a 10. dekády " .. century1 .. ". století"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "přelom " .. century1 .. ". a " .. century2 .. ". století"
else
return "přelom " .. decade1 .. "0. a " .. decade2 .. "0. let " .. century1 .. ". století"
end
end,
de='späte $date1er und frühe $date2er',
ckb='کۆتایییەکانی $date1 و سەرەتاکانی $date2',
en='late $date1s and early $date2s',
['en-gb']='late $date1s and early $date2s',
['en-ca']='late $date1s and early $date2s',
eo='ŝanĝo de la $date1-a al la $date2-a jardekoj',
es='finales de los años $date1 y principios de los años $date2',
fa='اواخر دههٔ $date1 و اوایل دههٔ $date2',
fi='$date1-luvun loppu ja $date2-luvun alku',
fr='fin des années $date1 / début des années $date2',
hr='kraj $date1-ih i početak $date2-ih',
hsb='pózdnje $date1te a zažne $date2te lěta',
it='fine degli anni $date1 / inizio degli anni $date2',
ja='$date1年代末から$date2年代初頭',
ml='$date1കൾ അവസാനവും $date2കൾ ആദ്യവും',
nb = 'sent på $date1-tallet og tidlig på $date2-tallet',
nl = 'eind $date1e/begin $date2e decennium',
nn = 'seint på $date1-talet og tidleg på $date2-talet',
pl=function(date1, date2, state)
local decade1 = math.floor(date1 % 100)
local decade2 = math.floor(date2 % 100)
local century1 = Roman(math.floor( date1 / 100) + 1)
local century2 = Roman(math.floor( date2 / 100) + 1)
if decade1 == 0 and date1 < date2 then
return "przełom pierwszej i drugiej dekady " .. century1 .. " wieku"
elseif decade1 == 10 and date1 < date2 then
return "przełom drugiej dekady i lat 20. " .. century1 .. " wieku"
elseif decade1 == 90 and decade2 == 0 then
return "przełom ostatniej dekady " .. century1 .. " i pierwszej dekady " .. century2 .. " wieku"
else
return "przełom lat " .. decade1 .. ". i " .. decade2 .. ". " .. century1 .. " wieku"
end
end,
qu='qhipaq $date1 watakuna ñawpaq $date2 watakunapas',
ru='конец $date1-х — начало $date2-х гг.',
sv='sena $date1-talet och tidiga $date2-talet',
sk=function(date1, date2, state)
local decade1 = math.floor((date1 % 100) / 10)
local decade2 = math.floor((date2 % 100) / 10)
local century1 = math.floor( date1 / 100) + 1
local century2 = math.floor( date2 / 100) + 1
if decade1 == 0 and date1 < date2 then
return "prelom 1. a 2. desaťročia " .. century1 .. ". storočia"
elseif decade2 == 0 and date2 < date1 then
return "prelom 9. a 10. desaťročia " .. century1 .. ". storočia"
elseif (decade1 == 9 and date1 < date2) or (decade2 == 9 and date2 < date1) then
return "prelom " .. century1 .. ". a " .. century2 .. ". storočia"
else
return "prelom " .. decade1 .. "0. a " .. decade2 .. "0. rokov " .. century1 .. ". storočia"
end
end,
sl='pozna $date1-ta in zgodnja $date2-ta leta',
uk='кінець $date1-х — початок $date2-х рр.',
ur='اخیر دہائی $date1 و ابتدا دہائی $date2',
vi='bước sang thập niên $date2',
['zh-hans']='$date1年代末$date2年代初',
['zh-hant']='$date1年代末$date2年代初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='decade', era1=state.era1, adj2 = 'early', unit2='decade', era2=state.era2} end,
},
-- $date1 - original date - 1
-- $date2 - original date
-- if era is BC than $date1 and $date2 are swapped
['turn of the year']={
nDates = 2,
['be-tarask']='канец $date1 — пачатак $date2 г.',
bn='$date1-এর শেষে এবং $date2-এর শুরুতে',
ckb='کۆتاییی $date1 و سەرەتای $date2',
cs='přelom roku $date1/$date2',
de='Jahreswechsel $date1/$date2',
en='late $date1 and early $date2',
['en-gb']='late $date1 and early $date2',
['en-ca']='late $date1 and early $date2',
eo='jarŝanĝo $date1 / $date2',
es='finales de $date1 y principios de $date2',
fa='اواخر $date1 و اوایل $date2',
fi='vuoden $date1 loppu ja vuoden $date2 alku',
fr='fin $date1 / début $date2',
hsb='kónc $date1/spočatk $date2',
ja='$date1年末から$date2年初め',
ml='$date1 അവസാനവും $date2 ആദ്യവും',
nb='årsskiftet $date1/$date2',
nl='eind $date1 en begin $date2',
nn='årsskiftet $date1/$date2',
pl='koniec $date1 i początek $date2 roku',
qu="$date1 qhipaq p'unchawkuna $date2 ñawpaq p'unchawkunapas",
ru='конец $date1 — начало $date2 г.',
sk='prelom rokov $date1/$date2',
sl='konec leta $date1 in začetek $date2',
sv='sena $date1 och tidiga $date2',
uk='кінець $date1 — початок $date2 р.',
ur='اخیر $date1 و ابتدا $date2',
vi='bước sang năm $date2',
['zh-hans']='$date1年末$date2年初',
['zh-hant']='$date1年末$date2年初',
default=function(date1, date2, state) return {conj='and', adj1 = 'late', unit1='year', era1=state.era1, adj2 = 'early', unit2='year', era2=state.era2} end,
},
['circa2'] = {
nDates = 2,
ar='حوالي $date1 – $date2',
['be-tarask']={'прыкладна з $date1 да прыкладна $date2', case={'gen','gen'}},
bn='প্রায় $date1 এবং প্রায় $date2-এর মাঝামাঝি',
ca='cap a $date1 o $date2',
ckb='بە نزیکی لەنێوان $date1 و $date2',
cs='cca $date1 až $date2',
da='ca. mellem $date1 og $date2',
de='etwa $date1 bis $date2',
el='περίπου μεταξύ $date1 και $date2',
en='between c. $date1 and c. $date2',
['en-gb']='between circa $date1 and circa $date2',
['en-ca']='between circa $date1 and circa $date2',
eo='proksimume de $date1 ĝis $date2',
es='hacia $date1 o $date2',
et='ca. $date1 ja $date2 vahel',
fa='حدوداً بین $date1 و $date2',
fi={function(date1, date2, state) return 'suunnilleen ' .. decYear(state,'fi','xxx') .. '$date1 ja $date2 välisenä aikana' end, case={'gen','gen'}},
fr='vers $date1-$date2',
gl='entre $date1 e $date2',
he='בסביבות ה- $date1 עד $date2',
hr='oko između $date1 i $date2',
hsb={'wot něhdźe $date1 do něhdźe $date2', case={'gen','gen'}},
hu='$date1 és $date2 között',
it='tra il $date1 e il $date2 circa',
ja='$date1頃と$date2頃の間',
mk='~ помеѓу $date1 и $date2',
ml='ഉദ്ദേശം $date1-നും ഉദ്ദേശം $date2-നും ഇടയ്ക്ക്',
nb='ca. mellom $date1 og $date2',
nn='ca. mellom $date1 og $date2',
nds='üm un bi $date1 bet $date2',
nl='ca. $date1 tot $date2',
no='ca. mellom $date1 og $date2',
pl={'od około $date1 do około $date2', case={'gen','gen'}},
pt='cerca de $date1 a $date2',
qu="$date1 p'unchawmantachá $date2 p'unchawkamachá",
ro='circa $date1 – $date2',
ru={'с около $date1 до, примерно, $date2', case={'gen','gen'}},
sk='cca $date1 až $date2',
sl={function(date1, date2, state) return 'ok. ' .. decYear(state,'sl','gen') .. ' $date1 do $date2' end, case={'gen','gen'}},
sv='c:a $date1 till $date2',
uk={'з близько $date1 до, приблизно, $date2', case={'gen','gen'}},
ur='قریباً بین $date1 و $date2',
vi='giữa vào khoảng $date1 và vào khoảng $date2',
['zh-hans']='约$date1~$date2间',
['zh-hant']='約$date1~$date2間',
default=function(date1, date2, state) return {conj='between', adj1 = 'circa', unit1=state.unit1, era1=state.era1, adj2 = 'circa', unit2=state.unit2, era2=state.era2} end,
},
-- ==========================================
-- === Eras and calendars ===================
-- ==========================================
['julian'] = {
nDates = 2,
ar='$date1 ($date2 حسب [[w:ar:التقويم اليولياني|تقويم يولياني]])',
['be-tarask']='$date1 ($date2 паводле [[w:be-tarask:Юліянскі каляндар|ст. ст.]]',
bn='$date1 (জুলিয়ান ক্যালেন্ডারে $date2)',
ca='$date1 ($date2 segons el [[w:ca:Calendari julià|calendari julià]])',
ckb='$date1 ($date2 بەپێی [[w:ڕۆژژمێری جولیانی|ڕۆژژمێری جولیانی]])',
cs='$date1 ($date2 podle [[w:cs:Juliánský kalendář|juliánského kalendáře]])',
da='$date1 ($date2 [[w:da:Julianske kalender|Julianske kalender]])',
de='$date1 ($date2 [[w:de:Julianischer Kalender|Julianischer Kalender]])',
el='$date1 ($date2 με το [[w:el:Ιουλιανό ημερολόγιο|Ιουλιανό ημερολόγιο]])',
en='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-gb']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
['en-ca']='$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])',
fa='$date1 ($date2 در [[w:fa:Old Style and New Style dates|تقویم جولیان]])',
eo='$date1 ($date2 laŭ la [[w:eo:Julia kalendaro|Julia Kalendaro]])',
es='$date1 ($date2 según el [[w:es:Calendario juliano|calendario juliano]])',
et='$date1 ($date2 [[w:et:Juliuse kalender|Juliuse kalendris]])',
fi='$date1 ($date2 [[w:fi:juliaaninen kalenteri|juliaanisen kalenterin]] mukaan)',
fr='$date1 ($date2 dans le [[w:fr:calendrier julien|calendrier julien]])',
gl='$date1 ($date2 segundo o [[w:gl:Calendario xuliano|calendario xuliano]])',
hu='$date1 ($date2 a [[w:hu:Julianus-naptár|Julián naptárban]])',
hr='$date1 ($date2 prema [[w:hr:Julijanski kalendar|julijanskom kalendaru]])',
he='$date1 ($date2 ב[[w:he:לוח היוליאני|הלוח היוליאני]])',
hsb='$date1 ($date2 po julianiskej protyce)',
it='$date1 ($date2 secondo il [[w:it:Calendario giuliano|calendario giuliano]])',
ja='$date1([[w:ja:ユリウス暦|ユリウス暦]] $date2)',
mk='$date1 ($date2 по [[w:mk:Јулијански календар|Јулијанскиот календар]])',
ml='$date1 ([[w:ml:ജൂലിയൻ കാലഗണനാരീതി|ജൂലിയൻ വർഷത്തിൽ]] $date2)',
ml='$date1 ([[w:Old Style and New Style dates|ജൂലിയൻ കാലഗണനാരീതിയിൽ]] $date2)',
nds='$date1 ($date2 na’n [[w:nds:Juliaansch Klenner|Juliaanschen Klenner]])',
nb='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nn='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalenderen]])',
nl='$date1 ($date2 volgens de [[w:nl:Juliaanse kalender|Juliaanse kalender]])',
no='$date1 ($date2 etter [[w:no:Juliansk kalender|den julianske kalender]])',
pl='$date1 ($date2 wedle [[w:pl:Daty nowego i starego porządku|kalendarza juliańskiego]])',
pt='$date1 ($date2 no [[w:pt:Mudança para o calendário gregoriano|calendário juliano]])',
qu='$date1 ($date2 [[w:qu:Hulyanu kalindaryu|Hulyanu kalindaryupi]])',
ro='$date1 ($date2 în [[w:ro:calendarul iulian|calendarul iulian]])',
ru='$date1 ($date2 [[w:ru:Юлианский календарь|по ст. ст.]])',
sk='$date1 ($date2 podľa [[w:sk:Juliánsky kalendár|juliánskeho kalendára]])',
sl='$date1 ($date2 po [[w:sl:Julijanski koledar|julijanskem koledarju]])',
sv='$date1 ($date2 [[w:sv:Gamla stilen|gamla stilen]])',
uk='$date1 ($date2 [[w:uk:Юліанський календар|Юліанський календар]])',
ur='$date1 ($date2 حسب [[w:ur:نئے اور پانے انداز کی تاریخ|جولین تقویم]])',
vi='$date1 ($date2 trong [[w:vi:Lịch Julius|lịch Juliêng]])',
['zh-hans']='$date1([[w:Old Style and New Style dates|格里历]]$date2)',
['zh-hant']='$date1([[w:Old Style and New Style dates|格里曆]]$date2)',
default='<span style="color:red">$date1 ($date2 in [[w:Old Style and New Style dates|Julian calendar]])</span>',
},
['ad'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:ад Нараджэньня Хрыста|пасьля Н. Х.]]',
bn ='$date1 খ্রিস্টাব্দ',
ca ='$date1 dC',
ckb ='$date1 [[w:ckb:ئانۆ دۆمینی|پاز]]',
cs ='$date1 n. l.',
de ='$date1 n. Chr.',
el ='$date1 μ.Χ.',
en ='$date1 [[w:en:Anno Domini|AD]]',
['en-gb'] ='$date1 [[w:en:Anno Domini|AD]]',
['en-ca'] ='$date1 [[w:en:Anno Domini|AD]]',
eo ='$date1 pK',
es ='$date1 [[w:es:Anno Dómini|d. C.]]',
fa ='$date1 م.',
fi ='$date1 jaa.',
fr ='$date1 ap. J.-C.',
gl ='$date1 d.C.',
he ='$date1 לספירה',
hu ='i. sz. $date1',
hr ='$date1 p. Kr.',
hsb='$date1 po Chr.',
it ='$date1 d.C.',
ja ='西暦$date1',
mk ='$date1 н.е.',
ml ='ക്രി.വ. $date1',
nb ='$date1 e.Kr.',
nl ='$date1 n.Chr.',
nn ='$date1 e.Kr.',
no ='$date1 e.Kr.',
pl ='$date1 [[w:pl:naszej ery|n.e.]]',
ro ='$date1 d.Hr.',
ru ='$date1 н. э.',
sk ='$date1 po Kr.',
sl ='$date1 n. št.',
sv ='$date1 [[w:sv:Efter Kristus|e.Kr.]]',
uk ='$date1 н. е.',
ur ='$date1 ء',
vi ='$date1 [[w:vi:Công Nguyên|CN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元]]$date1',
default='<span style="color:red">$date1 [[w:en:Anno Domini|AD]]</span>',
},
['ah'] = {
nDates = 1,
['be-tarask']='$date1 [[w:be-tarask:Ісламскі каляндар|хіджры]]',
bn ='$date1 [[w:bn:ইসলামি বর্ষপঞ্জি|হিজরী]]',
ckb ='$date1 [[w:ckb:ساڵی کۆچی|سک]]',
cs ='$date1 [[w:cs:Anno Hegirae|AH]]',
en ='$date1 [[w:en:Hijri year|AH]]',
['en-gb'] ='$date1 [[w:en:Hijri year|AH]]',
['en-ca'] ='$date1 [[w:en:Hijri year|AH]]',
eo ='$date1 post la [[w:eo:Heĝiro|Heĝiro]]',
fa ='$date1 ه.ق.',
fr ='$date1 [[w:fr:Calendrier musulman|A.H.]]',
he ="$date1 [[w:he:הלוח המוסלמי|להג'רה]]",
hr ='$date1 god. hidžre',
ja ='[[w:ja:ヒジュラ暦|ヒジュラ暦]]$date1',
ml ='$date1 [[w:ml:ഇസ്ലാമിക കലണ്ടർ|ഹിജ്റ]]',
nb ='$date1 [[w:no:Den muslimske kalenderen|A.H.]]',
nn ='$date1 [[w:nn:Den muslimske kalenderen|A.H.]]',
pl ='$date1 [[w:pl:Kalendarz muzułmański|A.H.]]',
ru ='$date1 [[w:ru:Исламский календарь|хиджры]]',
sk ='$date1 [[w:sk:A. H.|A. H.]]',
sv ='$date1 [[w:sv:Muslimska kalendern|A.H.]]',
uk ='$date1 [[w:uk:Мусульманський календар|хіджри]]',
ur ='$date1 ھ',
vi ='$date1 [[w:vi:Lịch Hồi giáo|AH]]',
['zh-hans'] ='[[w:en:Hijri year|Hijri历]]$date1',
['zh-hant'] ='[[w:en:Hijri year|Hijri曆]]$date1',
default='<span style="color:red">$date1 [[w:en:Hijri year|AH]]</span>',
},
['bc'] = {
nDates = 1,
ar ='$date1 ق.م.',
['be-tarask']='$date1 [[w:be-tarask:да Нараджэньня Хрыста|да Н. Х.]]',
bn ='খ্রিস্টপূর্ব $date1',
ca ='$date1 [[w:ca:Abans de Crist|aC]]',
ckb ='$date1 [[w:ckb:پێش زایین|پز]]',
cs ='$date1 [[w:cs:Př. n. l.|př. n. l.]]',
de ='$date1 [[w:de:v. Chr.|v. Chr.]]',
el ='$date1 π.Χ.',
en ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-gb'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
['en-ca'] ='$date1 [[w:en:Ante Christum Natum|BC]]',
eo ='$date1 aK',
es ='$date1 [[w:es:Antes de Cristo|a. C.]]',
fa ='$date1 پ.م.',
fi ='$date1 eaa.',
fr ='$date1 av. J.-C.',
gl ='$date1 [[w:gl:Antes de Cristo|a.C.]]',
he ='$date1 לפני הספירה',
hr ='$date1 [[w:hr:Pr. Kr.|pr. Kr.]]',
hsb='$date1 do Chr.',
hu ='i. e. $date1',
it ='$date1 [[w:it:Avanti Cristo|a.C.]]',
ja ='[[w:ja:紀元前|紀元前]]$date1',
mk ='$date1 п.н.е.',
ml ='ക്രി.മു. $date1',
nds='$date1 vör uns Tied',
nb ='$date1 f.Kr.',
nl ='$date1 v.Chr.',
nn ='$date1 f.Kr.',
no ='$date1 f.Kr.',
pl ='$date1 [[w:pl:przed naszą erą|p.n.e.]]',
ro ='$date1 î.Hr.',
ru ='$date1 до н. э.',
sk ='$date1 [[w:sk:Pred Kristom|pred Kr.]]',
sl ='$date1 pr. n. št.',
sv ='$date1 [[w:sv:Före Kristus|f.Kr.]]',
uk ='$date1 до н. е.',
ur ='$date1 ق م',
vi ='$date1 [[w:vi:Trước Công Nguyên|TCN]]',
['zh-hans'] ='[[w:zh:基督纪年|公元前]]$date1',
['zh-hant'] ='[[w:zh:基督纪年|公元前]]$date1',
default='<span style="color:red">$date1 [[w:en:Ante Christum Natum|BC]]</span>',
},
['bp'] = {
nDates = 1,
ar ='$date1 [[w:ar:قبل الحاضر|قبل الحاضر]]',
be ='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
['be-tarask']='$date1 [[w:be-tarask:Да нашых дзён|Да нашых дзён]]',
bn ='বর্তমানের আগে $date1',
ca ='$date1 [[w:ca:BP (unitat de temps)|BP]]',
ckb ='$date1 [[w:ckb:پێش ئێستا|پێش ئێستا]]',
cs ='$date1 [[w:cs:B. P.|B. P.]]',
de ='$date1 [[w:de:Before Present|BP]]',
el ='$date1 [[w:en:Before Present|BP]]',
en ='$date1 [[w:en:Before Present|BP]]',
['en-gb'] ='$date1 [[w:en:Before Present|BP]]',
['en-ca'] ='$date1 [[w:en:Before Present|BP]]',
eo='$date1 [[w:eo:Before Present|BP]]',
es ='$date1 [[w:es:Before Present|BP]]',
et ='$date1 [[w:et:Before Present|BP]]',
fa ='$date1 پیش از اکنون',
fi ='$date1 [[w:fi:BP (ajanlasku)|BP]]',
fr ='$date1 [[w:fr:Avant le présent|avant le présent]]',
hr ='$date1 [[w:hr:BP|prije današnjice]]',
it ='$date1 [[w:it:Before Present|BP]]',
ja ='$date1 [[w:ja:BP (年代測定)|BP]]',
lv ='$date1 [[w:lv:Pirms mūsdienām|Pirms mūsdienām]]',
mk ='$date1 [[w:mk:Пред сегашноста|п.с.]]',
ml ='[[w:en:Before Present|ഇന്നത്തെ കാലത്തിനു മുമ്പ്]] $date1',
nb ='$date1 [[w:no:Before Present|BP]]',
nn ='$date1 [[w:nn:Before Present|BP]]',
nl ='$date1 [[w:nl:Before Present|BP]]',
no ='$date1 [[w:no:Before Present|BP]]',
pl ='$date1 [[w:pl:Before Present|BP]]',
pt ='$date1 [[w:pt:Antes do Presente|Antes do Presente]]',
ru ='$date1 [[w:ru:До настоящего времени|до н. в.]]',
sk ='$date1 [[w:sk:BP (čas)|BP]]',
sl ='$date1 [[w:sl:pred sedanjostjo|p. s.]]',
sr ='$date1 [[w:sr:Прије садашњости|Прије садашњости]]',
sv ='$date1 [[w:sv:B.P.|B.P.]]',
uk ='$date1 [[w:uk:Років тому|Років тому]]',
ur ='$date1 قبل حاضر',
vi ='$date1 [[w:en:Before Present|trước ngày nay]]',
['zh-hans'] ='[[w:zh:距今|距今]]$date1',
['zh-hant'] ='[[w:zh:距今|距今]]$date1',
default='<span style="color:red">$date1 [[w:en:Before Present|BP]]</span>',
},
}
-- List of possible string inputs to "adj", "junction", "era", and "precision" fields
-- Each term is matched to its synonym and the role: a - adjective, e - era
-- p - precision, c - calendar and combos, r - certainty and j - conjunction
p.Synonyms = {
-- adjectives and prepositions
['quarter1']={'1quarter','a'},
['1stquarter']={'1quarter','a'},
['1st quarter']={'1quarter','a'},
['first quarter']={'1quarter','a'},
['1quarter']={'1quarter','a'},
['quarter2']={'2quarter','a'},
['2ndquarter']={'2quarter','a'},
['2nd quarter']={'2quarter','a'},
['second quarter']={'2quarter','a'},
['2quarter']={'2quarter','a'},
['quarter3']={'3quarter','a'},
['3rdquarter']={'3quarter','a'},
['3rd quarter']={'3quarter','a'},
['third quarter']={'3quarter','a'},
['3quarter']={'3quarter','a'},
['quarter4']={'4quarter','a'},
['4thquarter']={'4quarter','a'},
['4th quarter']={'4quarter','a'},
['fourth quarter']={'4quarter','a'},
['4quarter']={'4quarter','a'},
['spring']={'spring','a'},
['summer']={'summer','a'},
['fall']={'autumn','a'},
['autumn']={'autumn','a'},
['winter']={'winter','a'},
['beginning']={'early','a'},
['early']={'early','a'},
['1half']={'firsthalf','a'},
['1sthalf']={'firsthalf','a'},
['1st half']={'firsthalf','a'},
['firsthalf']={'firsthalf','a'},
['first half']={'firsthalf','a'},
['end']={'late','a'},
['late']={'late','a'},
['middle']={'mid','a'},
['mid']={'mid','a'},
['2half']={'secondhalf','a'},
['2ndhalf']={'secondhalf','a'},
['2nd half']={'secondhalf','a'},
['secondhalf']={'secondhalf','a'},
['second half']={'secondhalf','a'},
['<']={'before','a'},
['b']={'before','a'},
['before']={'before','a'},
['>']={'after','a'},
['a']={'after','a'},
['after']={'after','a'},
['~']={'circa','a'},
['c']={'circa','a'},
['ca']={'circa','a'},
['circa']={'circa','a'},
['by']={'by','a'},
['from']={'from','a'},
['until']={'until','a'},
['uncertain']={'uncertain','r'},
['possibly']={'possibly','r'},
['probably']={'probably','r'},
['presumably']={'presumably','r'},
-- eras
['bc']={'bc','e'},
['bce']={'bc','e'},
['before present']={'bp','e'},
['bp']={'bp','e'},
['ce']={'ad','e'},
['ad']={'ad','e'},
['ah']={'ah','e'},
-- conjunctions
['&']={'and','j'},
['and']={'and','j'},
['or']={'or','j'},
['-']={'from-until','j'},
['–']={'from-until','j'},
['from-until']={'from-until','j'},
['between']={'between','j'},
['circa2']={'circa2','a'},
-- precision
['day']={'day','p'},
['month']={'month','p'},
['year']={'year','p'},
['s']={'decade','p'},
['decade']={'decade','p'},
['century']={'century','p'},
['millennium']={'millennium','p'},
['million']={'million','p'},
['billion']={'billion','p'},
-- calendar
['julian']={'julian','c'},
['islamic']={'islamic','c'},
['hijri']={'islamic','c'},
-- "Turn of the ..." options are a combos which will be handled in similar way as calendars
['turn of the century']={'turn of the century','c'},
['century turn']={'turn of the century','c'},
['turn of the decade']={'turn of the decade','c'},
['decade turn']={'turn of the decade','c'},
['turn of the year']={'turn of the year','c'},
['year turn']={'turn of the year','c'},
-- other dates which will be handled in similar way as calendars
['?']={'unknown','c'},
['unknown']={'unknown','c'},
['unknown year']={'year unknown','c'},
['year unknown']={'year unknown','c'},
}
return p
dq6g8vtfaua8zgt63ytoeyms0jwcsm1
Module:ISOdate
828
65954
803603
634135
2019-01-24T01:40:44Z
en>Mike Peel
0
Protected "[[Module:ISOdate]]": [[WP:High-risk templates|High-risk Lua module]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
634134
Scribunto
text/plain
--[[
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:ISOdate/sandbox and testing
at Module:ISOdate/sandbox/testcases and Module talk:ISOdate/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date and template:ISOyear
]]
local p = {}
-- =======================================
-- === Dependencies ======================
-- =======================================
local D = require('Module:DateI18n') -- the enwp version of c:Module:Date
--[[
ISOyear
This function returns year part of date string.
Usage:
{{#invoke:ISOdate|ISOyear|target_string}}
Parameters
1: The date string
Error Handling:
If the string does not look like it contain the year than the function will not return anything.
That is the preferred treatment for the template:Creator which is the main (only?) template calling it.
]]
function p.ISOyear( frame )
return p._ISOyear( frame.args[1] )
end
function p._ISOyear( input )
if not input then
return ''
end
input = mw.text.trim( input )
-- if empty string then return it
if input == "" then
return input
end
-- if number then return it
if tonumber( input ) then
return mw.ustring.format( '%04i', input )
end
-- otherwise use regular expression match
input = mw.ustring.match( input, '^+?(-?%d%d?%d?%d?)-' )
if input and tonumber( input ) then
return mw.ustring.format( '%04i', input )
else
return ''
end
end
--[[
ISOdate
This function is the core part of the ISOdate template.
Usage:
{{#invoke:ISOdate|ISOdate|target_string|lang=}}
Parameters:
1: The date string
lang: The language to display it in
form: Language format (genitive, etc.) for some languages
class: CSS class for the <time> node
Error Handling:
If the string does not look like it contain the proper ISO date than the function will return the original string.
That is the preferred treatment for the template:Information (and similar templates) which calling it.
]]
function p.ISOdate(frame)
local datestr, succeded
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
datestr, succeded = p._ISOdate(
mw.text.trim(args[1]),
args.lang, -- language
args.case or '', -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
return datestr
end
function p._ISOdate(datestr, lang, case, class, trim_year)
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
-- regexp hints:
-- 1) Strings starting with "^" and ending with "$" indicate whole string match
-- 2) optional tail part copied as-is and following the main parsed part of the date have to be separated from the date by a whitespace, so "(\s.+)?"
local patterns = {
-- strings starting with YYYY-MM-DD HH:MM:SS. Year 4 digits (if we know seconds than it was within the last 100 years), the rest 1-2
-- date and time can be separated by space or "T" and there could be a "Z" on the end indicating "Zulu" time zone
{dlen=6, tail=7, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?):(%d%d?)Z?(%s.*)"},
{dlen=6, tail=0, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?):(%d%d?)Z?$"},
-- strings starting with YYYY-MM-DD HH:MM. Year 4 digits, the rest 1-2
-- (if one knows hour and minute than it was probably after a year 1000)
{dlen=5, tail=6, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)(%s.+)"},
{dlen=5, tail=0, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)$"},
-- strings starting with YYYY-MM-DD. Year 1-4 digits, the rest 1-2
{dlen=3, tail=4, regexp="^+?(%d%d?%d?%d?)-(%d%d?)-(%d%d?)(%s.+)"},
{dlen=3, tail=0, regexp="^+?(%d%d?%d?%d?)-(%d%d?)-(%d%d?)$"},
-- strings starting with YYYY-MM. Year 3-4 digits, month 2 digits
-- (want to avoit converting to dates strings like 10-5 = 5
{dlen=2, tail=3, regexp="^+?(%d%d%d%d?)-(%d%d)(%s.+)"},
-- if whole string is in YYYY-MM form: If Year 1-4 digits, month 1-2 digits
{dlen=2, tail=0, regexp="^+?(%d%d?%d?%d?)-(%d%d?)$"},
-- string starts with a number -> it has to be 3 or 4 digit long to be a year
{dlen=1, tail=2, regexp="^+?(%d%d%d%d?)(%s.+)"},
-- if whole string is a number (1-4 digit long) than it will be interpreted as a year
{dlen=1, tail=0, regexp="^+?(%d%d?%d?%d?)$"},
}
-- create datevec based on which variables are provided
local datevec, tail, formatNum
datevec, tail, formatNum = p.test_date_formats(datestr or '', patterns)
if datevec[1]=='' or datevec[1]==nil then
-- quickly return if datestr does not look like date (it could be a template)
return datestr, false
end
-- call p._Date function to format date string
local succeded, datestr2
succeded, datestr2 = pcall( D._Date, datevec, lang, case, class, trim_year)
if succeded and datestr2~='' then
return mw.text.trim( datestr2 .. tail), true
else -- in case of errors return the original string
return datestr, false
end
end
function p.ISOdate_extended(frame)
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
-- regexp hints:
-- 1) Strings starting with "^" and ending with "$" indicate whole string match
-- 2) optional tail part copied as-is and following the main parsed part of the date have to be separated from the date by a whitespace, so "(\s.+)?"
local datestr, succeded
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
datestr, succeded = p._ISOdate(
mw.text.trim(args[1]),
args.lang, -- language
args.case or '', -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
if succeded then
return datestr
end
local patterns = {
-- Exended set of recognized formats: like MM/DD/YYYY
{dlen=3, tail=4, regexp="^(%d%d?)[-./](%d%d?)[-./](%d%d%d%d)(%s.+)"},
{dlen=3, tail=0, regexp="^(%d%d?)[-./](%d%d?)[-./](%d%d%d%d)$"},
{dlen=3, tail=0, regexp="^(%d%d?)%s(%w+)%s(%d%d%d%d)$"},
{dlen=3, tail=0, regexp="^(%w+)%s(%d%d?),%s(%d%d%d%d)$"},
}
local datevec, tail, formatNum, category = ''
datevec, tail, formatNum = p.test_date_formats(frame.args[1], patterns)
if formatNum==1 or formatNum==2 then
vec = datevec;
if tonumber(datevec[1])>12 then
frame.args[1] = string.format('%04i-%02i-%02i', datevec[3], datevec[2], datevec[1] )
category = '[[Category:Date in DD/MM/YYYY format]]'
return mw.text.trim( p.ISOdate(frame) .. tail);
elseif tonumber(datevec[2])>12 then
frame.args[1] = string.format('%04i-%02i-%02i', datevec[3], datevec[1], datevec[2] )
category = '[[Category:Date in MM/DD/YYYY format]]'
return mw.text.trim( p.ISOdate(frame) .. tail);
end
elseif (formatNum==3 or formatNum==4) and (datevec[3]=='' or datevec[3]~=nil) then
local str = mw.getCurrentFrame():callParserFunction( "#time", { 'Y-m-d', datestr} )
local vec = {str:match( "^(%d%d?%d?%d?)-(%d%d?)-(%d%d?)$" )}
if vec and vec[1]~=nil then
frame.args[1] = string.format('%04i-%02i-%02i', vec[1], vec[2], vec[3] )
category = '[[Category:Date in word format]]'
return p.ISOdate(frame);
end
end
return datestr
end
function p.test_date_formats(datestr, patterns)
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
local datevec = {'','','','','',''}
local tail = ''
local vec, pat
local formatNum = 0
for i, pat in ipairs( patterns ) do
vec = {datestr:match( pat.regexp )}
if vec and vec[1]~=nil then
for j=1,pat.dlen do
datevec[j] = vec[j]
end
if pat.tail>0 and vec[pat.tail]~=nil then
tail = mw.ustring.gsub(' ' .. vec[pat.tail], ' +', ' ')
end
formatNum = i
break
end
end
return datevec, tail, formatNum
end
return p
nab04kxj0ywahk6drgfjdo6jbixhup7
803604
803603
2024-04-29T16:35:41Z
en>Pppery
0
Protected "[[Module:ISOdate]]" ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
634134
Scribunto
text/plain
--[[
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:ISOdate/sandbox and testing
at Module:ISOdate/sandbox/testcases and Module talk:ISOdate/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date and template:ISOyear
]]
local p = {}
-- =======================================
-- === Dependencies ======================
-- =======================================
local D = require('Module:DateI18n') -- the enwp version of c:Module:Date
--[[
ISOyear
This function returns year part of date string.
Usage:
{{#invoke:ISOdate|ISOyear|target_string}}
Parameters
1: The date string
Error Handling:
If the string does not look like it contain the year than the function will not return anything.
That is the preferred treatment for the template:Creator which is the main (only?) template calling it.
]]
function p.ISOyear( frame )
return p._ISOyear( frame.args[1] )
end
function p._ISOyear( input )
if not input then
return ''
end
input = mw.text.trim( input )
-- if empty string then return it
if input == "" then
return input
end
-- if number then return it
if tonumber( input ) then
return mw.ustring.format( '%04i', input )
end
-- otherwise use regular expression match
input = mw.ustring.match( input, '^+?(-?%d%d?%d?%d?)-' )
if input and tonumber( input ) then
return mw.ustring.format( '%04i', input )
else
return ''
end
end
--[[
ISOdate
This function is the core part of the ISOdate template.
Usage:
{{#invoke:ISOdate|ISOdate|target_string|lang=}}
Parameters:
1: The date string
lang: The language to display it in
form: Language format (genitive, etc.) for some languages
class: CSS class for the <time> node
Error Handling:
If the string does not look like it contain the proper ISO date than the function will return the original string.
That is the preferred treatment for the template:Information (and similar templates) which calling it.
]]
function p.ISOdate(frame)
local datestr, succeded
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
datestr, succeded = p._ISOdate(
mw.text.trim(args[1]),
args.lang, -- language
args.case or '', -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
return datestr
end
function p._ISOdate(datestr, lang, case, class, trim_year)
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
-- regexp hints:
-- 1) Strings starting with "^" and ending with "$" indicate whole string match
-- 2) optional tail part copied as-is and following the main parsed part of the date have to be separated from the date by a whitespace, so "(\s.+)?"
local patterns = {
-- strings starting with YYYY-MM-DD HH:MM:SS. Year 4 digits (if we know seconds than it was within the last 100 years), the rest 1-2
-- date and time can be separated by space or "T" and there could be a "Z" on the end indicating "Zulu" time zone
{dlen=6, tail=7, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?):(%d%d?)Z?(%s.*)"},
{dlen=6, tail=0, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?):(%d%d?)Z?$"},
-- strings starting with YYYY-MM-DD HH:MM. Year 4 digits, the rest 1-2
-- (if one knows hour and minute than it was probably after a year 1000)
{dlen=5, tail=6, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)(%s.+)"},
{dlen=5, tail=0, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)$"},
-- strings starting with YYYY-MM-DD. Year 1-4 digits, the rest 1-2
{dlen=3, tail=4, regexp="^+?(%d%d?%d?%d?)-(%d%d?)-(%d%d?)(%s.+)"},
{dlen=3, tail=0, regexp="^+?(%d%d?%d?%d?)-(%d%d?)-(%d%d?)$"},
-- strings starting with YYYY-MM. Year 3-4 digits, month 2 digits
-- (want to avoit converting to dates strings like 10-5 = 5
{dlen=2, tail=3, regexp="^+?(%d%d%d%d?)-(%d%d)(%s.+)"},
-- if whole string is in YYYY-MM form: If Year 1-4 digits, month 1-2 digits
{dlen=2, tail=0, regexp="^+?(%d%d?%d?%d?)-(%d%d?)$"},
-- string starts with a number -> it has to be 3 or 4 digit long to be a year
{dlen=1, tail=2, regexp="^+?(%d%d%d%d?)(%s.+)"},
-- if whole string is a number (1-4 digit long) than it will be interpreted as a year
{dlen=1, tail=0, regexp="^+?(%d%d?%d?%d?)$"},
}
-- create datevec based on which variables are provided
local datevec, tail, formatNum
datevec, tail, formatNum = p.test_date_formats(datestr or '', patterns)
if datevec[1]=='' or datevec[1]==nil then
-- quickly return if datestr does not look like date (it could be a template)
return datestr, false
end
-- call p._Date function to format date string
local succeded, datestr2
succeded, datestr2 = pcall( D._Date, datevec, lang, case, class, trim_year)
if succeded and datestr2~='' then
return mw.text.trim( datestr2 .. tail), true
else -- in case of errors return the original string
return datestr, false
end
end
function p.ISOdate_extended(frame)
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
-- regexp hints:
-- 1) Strings starting with "^" and ending with "$" indicate whole string match
-- 2) optional tail part copied as-is and following the main parsed part of the date have to be separated from the date by a whitespace, so "(\s.+)?"
local datestr, succeded
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
datestr, succeded = p._ISOdate(
mw.text.trim(args[1]),
args.lang, -- language
args.case or '', -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
if succeded then
return datestr
end
local patterns = {
-- Exended set of recognized formats: like MM/DD/YYYY
{dlen=3, tail=4, regexp="^(%d%d?)[-./](%d%d?)[-./](%d%d%d%d)(%s.+)"},
{dlen=3, tail=0, regexp="^(%d%d?)[-./](%d%d?)[-./](%d%d%d%d)$"},
{dlen=3, tail=0, regexp="^(%d%d?)%s(%w+)%s(%d%d%d%d)$"},
{dlen=3, tail=0, regexp="^(%w+)%s(%d%d?),%s(%d%d%d%d)$"},
}
local datevec, tail, formatNum, category = ''
datevec, tail, formatNum = p.test_date_formats(frame.args[1], patterns)
if formatNum==1 or formatNum==2 then
vec = datevec;
if tonumber(datevec[1])>12 then
frame.args[1] = string.format('%04i-%02i-%02i', datevec[3], datevec[2], datevec[1] )
category = '[[Category:Date in DD/MM/YYYY format]]'
return mw.text.trim( p.ISOdate(frame) .. tail);
elseif tonumber(datevec[2])>12 then
frame.args[1] = string.format('%04i-%02i-%02i', datevec[3], datevec[1], datevec[2] )
category = '[[Category:Date in MM/DD/YYYY format]]'
return mw.text.trim( p.ISOdate(frame) .. tail);
end
elseif (formatNum==3 or formatNum==4) and (datevec[3]=='' or datevec[3]~=nil) then
local str = mw.getCurrentFrame():callParserFunction( "#time", { 'Y-m-d', datestr} )
local vec = {str:match( "^(%d%d?%d?%d?)-(%d%d?)-(%d%d?)$" )}
if vec and vec[1]~=nil then
frame.args[1] = string.format('%04i-%02i-%02i', vec[1], vec[2], vec[3] )
category = '[[Category:Date in word format]]'
return p.ISOdate(frame);
end
end
return datestr
end
function p.test_date_formats(datestr, patterns)
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
local datevec = {'','','','','',''}
local tail = ''
local vec, pat
local formatNum = 0
for i, pat in ipairs( patterns ) do
vec = {datestr:match( pat.regexp )}
if vec and vec[1]~=nil then
for j=1,pat.dlen do
datevec[j] = vec[j]
end
if pat.tail>0 and vec[pat.tail]~=nil then
tail = mw.ustring.gsub(' ' .. vec[pat.tail], ' +', ' ')
end
formatNum = i
break
end
end
return datevec, tail, formatNum
end
return p
nab04kxj0ywahk6drgfjdo6jbixhup7
803605
803604
2026-07-30T00:19:19Z
SM7
3953
2 revisions imported from [[:en:Module:ISOdate]]
634134
Scribunto
text/plain
--[[
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:ISOdate/sandbox and testing
at Module:ISOdate/sandbox/testcases and Module talk:ISOdate/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date and template:ISOyear
]]
local p = {}
-- =======================================
-- === Dependencies ======================
-- =======================================
local D = require('Module:DateI18n') -- the enwp version of c:Module:Date
--[[
ISOyear
This function returns year part of date string.
Usage:
{{#invoke:ISOdate|ISOyear|target_string}}
Parameters
1: The date string
Error Handling:
If the string does not look like it contain the year than the function will not return anything.
That is the preferred treatment for the template:Creator which is the main (only?) template calling it.
]]
function p.ISOyear( frame )
return p._ISOyear( frame.args[1] )
end
function p._ISOyear( input )
if not input then
return ''
end
input = mw.text.trim( input )
-- if empty string then return it
if input == "" then
return input
end
-- if number then return it
if tonumber( input ) then
return mw.ustring.format( '%04i', input )
end
-- otherwise use regular expression match
input = mw.ustring.match( input, '^+?(-?%d%d?%d?%d?)-' )
if input and tonumber( input ) then
return mw.ustring.format( '%04i', input )
else
return ''
end
end
--[[
ISOdate
This function is the core part of the ISOdate template.
Usage:
{{#invoke:ISOdate|ISOdate|target_string|lang=}}
Parameters:
1: The date string
lang: The language to display it in
form: Language format (genitive, etc.) for some languages
class: CSS class for the <time> node
Error Handling:
If the string does not look like it contain the proper ISO date than the function will return the original string.
That is the preferred treatment for the template:Information (and similar templates) which calling it.
]]
function p.ISOdate(frame)
local datestr, succeded
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
datestr, succeded = p._ISOdate(
mw.text.trim(args[1]),
args.lang, -- language
args.case or '', -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
return datestr
end
function p._ISOdate(datestr, lang, case, class, trim_year)
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
-- regexp hints:
-- 1) Strings starting with "^" and ending with "$" indicate whole string match
-- 2) optional tail part copied as-is and following the main parsed part of the date have to be separated from the date by a whitespace, so "(\s.+)?"
local patterns = {
-- strings starting with YYYY-MM-DD HH:MM:SS. Year 4 digits (if we know seconds than it was within the last 100 years), the rest 1-2
-- date and time can be separated by space or "T" and there could be a "Z" on the end indicating "Zulu" time zone
{dlen=6, tail=7, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?):(%d%d?)Z?(%s.*)"},
{dlen=6, tail=0, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?):(%d%d?)Z?$"},
-- strings starting with YYYY-MM-DD HH:MM. Year 4 digits, the rest 1-2
-- (if one knows hour and minute than it was probably after a year 1000)
{dlen=5, tail=6, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)(%s.+)"},
{dlen=5, tail=0, regexp="^+?(%d%d%d%d)-(%d%d?)-(%d%d?)[ T](%d%d?):(%d%d?)$"},
-- strings starting with YYYY-MM-DD. Year 1-4 digits, the rest 1-2
{dlen=3, tail=4, regexp="^+?(%d%d?%d?%d?)-(%d%d?)-(%d%d?)(%s.+)"},
{dlen=3, tail=0, regexp="^+?(%d%d?%d?%d?)-(%d%d?)-(%d%d?)$"},
-- strings starting with YYYY-MM. Year 3-4 digits, month 2 digits
-- (want to avoit converting to dates strings like 10-5 = 5
{dlen=2, tail=3, regexp="^+?(%d%d%d%d?)-(%d%d)(%s.+)"},
-- if whole string is in YYYY-MM form: If Year 1-4 digits, month 1-2 digits
{dlen=2, tail=0, regexp="^+?(%d%d?%d?%d?)-(%d%d?)$"},
-- string starts with a number -> it has to be 3 or 4 digit long to be a year
{dlen=1, tail=2, regexp="^+?(%d%d%d%d?)(%s.+)"},
-- if whole string is a number (1-4 digit long) than it will be interpreted as a year
{dlen=1, tail=0, regexp="^+?(%d%d?%d?%d?)$"},
}
-- create datevec based on which variables are provided
local datevec, tail, formatNum
datevec, tail, formatNum = p.test_date_formats(datestr or '', patterns)
if datevec[1]=='' or datevec[1]==nil then
-- quickly return if datestr does not look like date (it could be a template)
return datestr, false
end
-- call p._Date function to format date string
local succeded, datestr2
succeded, datestr2 = pcall( D._Date, datevec, lang, case, class, trim_year)
if succeded and datestr2~='' then
return mw.text.trim( datestr2 .. tail), true
else -- in case of errors return the original string
return datestr, false
end
end
function p.ISOdate_extended(frame)
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
-- regexp hints:
-- 1) Strings starting with "^" and ending with "$" indicate whole string match
-- 2) optional tail part copied as-is and following the main parsed part of the date have to be separated from the date by a whitespace, so "(\s.+)?"
local datestr, succeded
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
datestr, succeded = p._ISOdate(
mw.text.trim(args[1]),
args.lang, -- language
args.case or '', -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
if succeded then
return datestr
end
local patterns = {
-- Exended set of recognized formats: like MM/DD/YYYY
{dlen=3, tail=4, regexp="^(%d%d?)[-./](%d%d?)[-./](%d%d%d%d)(%s.+)"},
{dlen=3, tail=0, regexp="^(%d%d?)[-./](%d%d?)[-./](%d%d%d%d)$"},
{dlen=3, tail=0, regexp="^(%d%d?)%s(%w+)%s(%d%d%d%d)$"},
{dlen=3, tail=0, regexp="^(%w+)%s(%d%d?),%s(%d%d%d%d)$"},
}
local datevec, tail, formatNum, category = ''
datevec, tail, formatNum = p.test_date_formats(frame.args[1], patterns)
if formatNum==1 or formatNum==2 then
vec = datevec;
if tonumber(datevec[1])>12 then
frame.args[1] = string.format('%04i-%02i-%02i', datevec[3], datevec[2], datevec[1] )
category = '[[Category:Date in DD/MM/YYYY format]]'
return mw.text.trim( p.ISOdate(frame) .. tail);
elseif tonumber(datevec[2])>12 then
frame.args[1] = string.format('%04i-%02i-%02i', datevec[3], datevec[1], datevec[2] )
category = '[[Category:Date in MM/DD/YYYY format]]'
return mw.text.trim( p.ISOdate(frame) .. tail);
end
elseif (formatNum==3 or formatNum==4) and (datevec[3]=='' or datevec[3]~=nil) then
local str = mw.getCurrentFrame():callParserFunction( "#time", { 'Y-m-d', datestr} )
local vec = {str:match( "^(%d%d?%d?%d?)-(%d%d?)-(%d%d?)$" )}
if vec and vec[1]~=nil then
frame.args[1] = string.format('%04i-%02i-%02i', vec[1], vec[2], vec[3] )
category = '[[Category:Date in word format]]'
return p.ISOdate(frame);
end
end
return datestr
end
function p.test_date_formats(datestr, patterns)
-- pattern: regexp - regular expresion to test; dlen - number of date elements; tail = which element is a "tail" if any
local datevec = {'','','','','',''}
local tail = ''
local vec, pat
local formatNum = 0
for i, pat in ipairs( patterns ) do
vec = {datestr:match( pat.regexp )}
if vec and vec[1]~=nil then
for j=1,pat.dlen do
datevec[j] = vec[j]
end
if pat.tail>0 and vec[pat.tail]~=nil then
tail = mw.ustring.gsub(' ' .. vec[pat.tail], ' +', ' ')
end
formatNum = i
break
end
end
return datevec, tail, formatNum
end
return p
nab04kxj0ywahk6drgfjdo6jbixhup7
Module:DateI18n
828
68207
803613
652109
2019-04-13T21:30:42Z
commons>Jarekt
0
clone of Module:Date, rename since all the other projects use this name
803613
Scribunto
text/plain
--[[
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date and template:ISOyear
]]
local p = {}
-- =======================================
-- === Dependencies ======================
-- =======================================
local i18n = require('Module:I18n/date') -- get localized translations of date formats
local yesno = require('Module:Yesno')
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
--[[
Date
This function is the core part of the ISOdate template.
Usage:
{{#invoke:Date|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
year,month,day,hour,minute,second: broken down date-time component strings
tzhour, tzmin: timezone offset from UTC, hours and minutes
lang: The language to display it in
case: Language format (genitive, etc.) for some languages
class: CSS class for the <time> node, use "" for no metadata at all
Error Handling:
]]
function p.Date(frame)
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
return p._Date(
{
args.year or '',
args.month or '',
args.day or '',
args.hour or '',
args.minute or '',
args.second or '',
args.tzhour or '',
args.tzmin or ''
},
args.lang, -- language
args.case or '', -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
for i = #datevec + 1, 8 do
datevec[i] = ''
end
if not case then case = '' end
if not class then class = '' end
if not trim_year then trim_year = '100-999' end
-- if language is not provided than look up users language
-- WARNING: This step should be done by the template as it does not seem to work as well here (cache issues?)
if not lang or not mw.language.isValidCode( lang ) then
lang = 'en'
end
-- Just in case someone broke the internationalization code than fix the english defaults
if i18n.DateLang['en'] == nil then
i18n.DateLang['en'] = 'en-form'
end
if i18n.DateFormat['en-form'] == nil then
i18n.DateFormat['en-form'] = {YMDHMS='j F Y, H:i:s', YMDHM='j F Y, H:i', YMD='j F Y', YM='F Y', MD='j F', Y='Y'}
end
-- create datecode based on which variables are provided and check for out of bound values
local maxval = {9999, 12, 31, 23, 59, 60, 23, 59} -- max values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i, v in ipairs( datevec ) do
if v~=nil and v~='' then
datecode = datecode .. c[i]
datenum[i] = tonumber(v)
if datenum[i]==nil and i==2 then
-- month is not a number -> check if it is a month name in English
v = mw.language.new('en'):formatDate( "n", v)
datenum[i] = tonumber(v)
end
if datenum[i]==nil or datenum[i]>maxval[i] then
-- Some numbers are out of range -> abort and return the empty string
return ''
end
end
end
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
class = '' -- date not complete -> no html formating or micro-tagging of date string
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
class = '' -- date not complete -> no html formating or micro-tagging of date string
else
return '' -- format not supported
end
-- ==========================================================
-- === Create Date String using in chosen language
-- ==========================================================
-- which form should the date take?
-- Use langSwitch to pick formating for each language
local langDateForm = langSwitch(i18n.DateLang, lang)
-- special case of French and Gallic dates, which require different date format for the 1st day of the month
if datenum[3]==1 and (langDateForm=='fr-form' or langDateForm=='ga-form' or langDateForm=='ca-form') then
langDateForm = langDateForm .. '1' -- ordinal form for the first day of the month
end
-- special case of Basque dates, which require different date format for the 1st, 11th, 21st and 31st day of the month
if langDateForm=='eu-form' then
if (datenum[3]==1 or datenum[3]==21) then
langDateForm = 'eu-form01'
elseif (datenum[3]==11 or datenum[3]==31) then
langDateForm = 'eu-form11'
end
end
-- Look up country specific format input to {{#time}} function
local dFormat = i18n.DateFormat[langDateForm][datecode]
-- overwrite default grammatical case of the month (applies mostly to Slavic languages)
if (case=='gen') then
-- CAUTION: at the moment i18n.DateFormat uses "F" only as month name, but this might change and this operation does not check if 'F' is in "" brackets or not, so if some language starts using 'F' in "" than this will not work for that language
dFormat = dFormat:gsub("F", "xg");
end
if (case=='nom') then
-- CAUTION: at the moment i18n.DateFormat uses "xg" only as month name, but this might change and this operation does not check if 'xg' is in "" brackets or not, so if some language starts using 'xg' in "" than this will not work for that language
dFormat = dFormat:gsub("xg", "F");
end
if ((lang=='ru' or lang=='pl' or lang=='cs' or lang=='sl' or lang=='sk') and (case=='loc' or case=='ins')) or
(lang=='fi' and (case=='ptv' or case=='ine'or case=='ela'or case=='ill') ) then
local monthEn = mw.language.new('en'):formatDate( "F", timeStamp) -- month name in English
-- month name using proper case and language. It relies on messages stored in MediaWiki namespace for some cases and languages
-- That is why this IF statement uses "lang" not "langDateForm" variable to decide
local monthMsg = mw.message.new( string.format('%s-%s', monthEn, case ) ):inLanguage( lang )
if not monthMsg:isDisabled() then -- make sure it exists
local month=monthMsg:plain()
dFormat = dFormat:gsub('F', '"'..month..'"'); -- replace default month with month name we already looked up
dFormat = dFormat:gsub('xg', '"'..month..'"');
end
end
-- Special case related to Quechua and Kichwa languages
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
if (lang=='qu' or lang=='qug') and case=='nom' then
dFormat = dFormat:gsub('F"pi"', 'F');
end
-- Lua only date formating using {{#time}} parser function (new)
-- prefered call which gives "Lua error: too many language codes requested." on the [[Module talk:Date/sandbox/testcases]] page
--local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp)
local datestr = mw.getCurrentFrame():callParserFunction( "#time", { dFormat, timeStamp, lang } )
-- Another special case related to Thai solar calendar
if lang=='th' and datenum[1]~= nil and datenum[1]<=1940 then
-- As of 2014 {{#time}} parser function did not resolve those cases properly
-- See https://en.wikipedia.org/wiki/Thai_solar_calendar#New_year for reference
-- Disable once https://bugzilla.wikimedia.org/show_bug.cgi?id=66648 is fixed
if datecode=='Y' then -- date is ambiguous
datestr = string.format('%04i หรือ %04i', datenum[1]+542, datenum[1]+543 )
elseif datenum[2]<=3 then -- year is wrong (one too many)
datestr = datestr:gsub( string.format('%04i', datenum[1]+543), string.format('%04i', datenum[1]+542 ) )
end
end
-- If year<1000 than either keep it padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if datenum[1]~= nil and datenum[1]<1000 then
local trim = yesno(trim_year,nil)
if trim == nil then
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and datenum[1]>=tonumber(YMin) and datenum[1]<=tonumber(YMax))
end
-- If the date form isn't the Thai solar calendar, don't zero pad years in the range of 100-999.
-- If at some point support for Islamic/Hebrew/Japanese years is added, they may need to be skipped as well.
if trim then
--local yearStr1 = mw.language.new(lang):formatDate( 'Y', timeStamp)
local yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', timeStamp, lang } )
--local yearStr1 = datestr:match( '%d%d%d%d' ) -- 4 digits in a row (in any language) - that must be a year
local yearStr2 = yearStr1
local zeroStr = mw.ustring.sub(yearStr1,1,1)
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
datestr = datestr:gsub( yearStr1, yearStr2 )
--datestr = string.format('%s (%s, %s)', datestr, yearStr1, yearStr2 )
end
end
-- append timezone if present
if datevec[7] ~= '' and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
local tzstr, tzhournum = '', tonumber(datevec[7])
if tzhournum < 0 then tzstr = '−' else tzstr = '+' end
tzstr = tzstr..string.format("%02d", math.abs(tzhournum))..':'
if datevec[8] ~= '' then tzstr = tzstr..datevec[8] else tzstr = tzstr..'00' end
datestr = datestr..' '..tzstr
end
-- html formating and tagging of date string
if class ~= '' then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
return p
n2fu3b16bq7r4kqhe08ddyc1nwz1tq8
803614
803613
2019-04-13T21:39:27Z
commons>Jarekt
0
803614
Scribunto
text/plain
--[[
__ __ _ _ ____ _ ___ _ ___
| \/ | ___ __| |_ _| | ___ _| _ \ __ _| |_ ___|_ _/ |( _ ) _ __
| |\/| |/ _ \ / _` | | | | |/ _ (_) | | |/ _` | __/ _ \| || |/ _ \| '_ \
| | | | (_) | (_| | |_| | | __/_| |_| | (_| | || __/| || | (_) | | | |
|_| |_|\___/ \__,_|\__,_|_|\___(_)____/ \__,_|\__\___|___|_|\___/|_| |_|
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date and template:ISOyear
]]
local p = {}
-- =======================================
-- === Dependencies ======================
-- =======================================
local i18n = require('Module:I18n/date') -- get localized translations of date formats
local yesno = require('Module:Yesno')
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
--[[
Date
This function is the core part of the ISOdate template.
Usage:
{{#invoke:DateI18n|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
year,month,day,hour,minute,second: broken down date-time component strings
tzhour, tzmin: timezone offset from UTC, hours and minutes
lang: The language to display it in
case: Language format (genitive, etc.) for some languages
class: CSS class for the <time> node, use "" for no metadata at all
Error Handling:
]]
function p.Date(frame)
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
return p._Date(
{
args.year or '',
args.month or '',
args.day or '',
args.hour or '',
args.minute or '',
args.second or '',
args.tzhour or '',
args.tzmin or ''
},
args.lang, -- language
args.case or '', -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
for i = #datevec + 1, 8 do
datevec[i] = ''
end
if not case then case = '' end
if not class then class = '' end
if not trim_year then trim_year = '100-999' end
-- if language is not provided than look up users language
-- WARNING: This step should be done by the template as it does not seem to work as well here (cache issues?)
if not lang or not mw.language.isValidCode( lang ) then
lang = 'en'
end
-- Just in case someone broke the internationalization code than fix the english defaults
if i18n.DateLang['en'] == nil then
i18n.DateLang['en'] = 'en-form'
end
if i18n.DateFormat['en-form'] == nil then
i18n.DateFormat['en-form'] = {YMDHMS='j F Y, H:i:s', YMDHM='j F Y, H:i', YMD='j F Y', YM='F Y', MD='j F', Y='Y'}
end
-- create datecode based on which variables are provided and check for out of bound values
local maxval = {9999, 12, 31, 23, 59, 60, 23, 59} -- max values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i, v in ipairs( datevec ) do
if v~=nil and v~='' then
datecode = datecode .. c[i]
datenum[i] = tonumber(v)
if datenum[i]==nil and i==2 then
-- month is not a number -> check if it is a month name in English
v = mw.language.new('en'):formatDate( "n", v)
datenum[i] = tonumber(v)
end
if datenum[i]==nil or datenum[i]>maxval[i] then
-- Some numbers are out of range -> abort and return the empty string
return ''
end
end
end
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
class = '' -- date not complete -> no html formating or micro-tagging of date string
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
class = '' -- date not complete -> no html formating or micro-tagging of date string
else
return '' -- format not supported
end
-- ==========================================================
-- === Create Date String using in chosen language
-- ==========================================================
-- which form should the date take?
-- Use langSwitch to pick formating for each language
local langDateForm = langSwitch(i18n.DateLang, lang)
-- special case of French and Gallic dates, which require different date format for the 1st day of the month
if datenum[3]==1 and (langDateForm=='fr-form' or langDateForm=='ga-form' or langDateForm=='ca-form') then
langDateForm = langDateForm .. '1' -- ordinal form for the first day of the month
end
-- special case of Basque dates, which require different date format for the 1st, 11th, 21st and 31st day of the month
if langDateForm=='eu-form' then
if (datenum[3]==1 or datenum[3]==21) then
langDateForm = 'eu-form01'
elseif (datenum[3]==11 or datenum[3]==31) then
langDateForm = 'eu-form11'
end
end
-- Look up country specific format input to {{#time}} function
local dFormat = i18n.DateFormat[langDateForm][datecode]
-- overwrite default grammatical case of the month (applies mostly to Slavic languages)
if (case=='gen') then
-- CAUTION: at the moment i18n.DateFormat uses "F" only as month name, but this might change and this operation does not check if 'F' is in "" brackets or not, so if some language starts using 'F' in "" than this will not work for that language
dFormat = dFormat:gsub("F", "xg");
end
if (case=='nom') then
-- CAUTION: at the moment i18n.DateFormat uses "xg" only as month name, but this might change and this operation does not check if 'xg' is in "" brackets or not, so if some language starts using 'xg' in "" than this will not work for that language
dFormat = dFormat:gsub("xg", "F");
end
if ((lang=='ru' or lang=='pl' or lang=='cs' or lang=='sl' or lang=='sk') and (case=='loc' or case=='ins')) or
(lang=='fi' and (case=='ptv' or case=='ine'or case=='ela'or case=='ill') ) then
local monthEn = mw.language.new('en'):formatDate( "F", timeStamp) -- month name in English
-- month name using proper case and language. It relies on messages stored in MediaWiki namespace for some cases and languages
-- That is why this IF statement uses "lang" not "langDateForm" variable to decide
local monthMsg = mw.message.new( string.format('%s-%s', monthEn, case ) ):inLanguage( lang )
if not monthMsg:isDisabled() then -- make sure it exists
local month=monthMsg:plain()
dFormat = dFormat:gsub('F', '"'..month..'"'); -- replace default month with month name we already looked up
dFormat = dFormat:gsub('xg', '"'..month..'"');
end
end
-- Special case related to Quechua and Kichwa languages
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
if (lang=='qu' or lang=='qug') and case=='nom' then
dFormat = dFormat:gsub('F"pi"', 'F');
end
-- Lua only date formating using {{#time}} parser function (new)
-- prefered call which gives "Lua error: too many language codes requested." on the [[Module talk:Date/sandbox/testcases]] page
--local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp)
local datestr = mw.getCurrentFrame():callParserFunction( "#time", { dFormat, timeStamp, lang } )
-- Another special case related to Thai solar calendar
if lang=='th' and datenum[1]~= nil and datenum[1]<=1940 then
-- As of 2014 {{#time}} parser function did not resolve those cases properly
-- See https://en.wikipedia.org/wiki/Thai_solar_calendar#New_year for reference
-- Disable once https://bugzilla.wikimedia.org/show_bug.cgi?id=66648 is fixed
if datecode=='Y' then -- date is ambiguous
datestr = string.format('%04i หรือ %04i', datenum[1]+542, datenum[1]+543 )
elseif datenum[2]<=3 then -- year is wrong (one too many)
datestr = datestr:gsub( string.format('%04i', datenum[1]+543), string.format('%04i', datenum[1]+542 ) )
end
end
-- If year<1000 than either keep it padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if datenum[1]~= nil and datenum[1]<1000 then
local trim = yesno(trim_year,nil)
if trim == nil then
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and datenum[1]>=tonumber(YMin) and datenum[1]<=tonumber(YMax))
end
-- If the date form isn't the Thai solar calendar, don't zero pad years in the range of 100-999.
-- If at some point support for Islamic/Hebrew/Japanese years is added, they may need to be skipped as well.
if trim then
--local yearStr1 = mw.language.new(lang):formatDate( 'Y', timeStamp)
local yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', timeStamp, lang } )
--local yearStr1 = datestr:match( '%d%d%d%d' ) -- 4 digits in a row (in any language) - that must be a year
local yearStr2 = yearStr1
local zeroStr = mw.ustring.sub(yearStr1,1,1)
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
datestr = datestr:gsub( yearStr1, yearStr2 )
--datestr = string.format('%s (%s, %s)', datestr, yearStr1, yearStr2 )
end
end
-- append timezone if present
if datevec[7] ~= '' and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
local tzstr, tzhournum = '', tonumber(datevec[7])
if tzhournum < 0 then tzstr = '−' else tzstr = '+' end
tzstr = tzstr..string.format("%02d", math.abs(tzhournum))..':'
if datevec[8] ~= '' then tzstr = tzstr..datevec[8] else tzstr = tzstr..'00' end
datestr = datestr..' '..tzstr
end
-- html formating and tagging of date string
if class ~= '' then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
return p
bmb66i0znfhfpeiuqiilu43abfhwhg5
803615
803614
2020-04-12T12:23:16Z
commons>Jarekt
0
use [[Data:DateI18n.tab]] as main repository of language dependent settings
803615
Scribunto
text/plain
--[[
__ __ _ _ ____ _ ___ _ ___
| \/ | ___ __| |_ _| | ___ _| _ \ __ _| |_ ___|_ _/ |( _ ) _ __
| |\/| |/ _ \ / _` | | | | |/ _ (_) | | |/ _` | __/ _ \| || |/ _ \| '_ \
| | | | (_) | (_| | |_| | | __/_| |_| | (_| | || __/| || | (_) | | | |
|_| |_|\___/ \__,_|\__,_|_|\___(_)____/ \__,_|\__\___|___|_|\___/|_| |_|
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date
]]
require('Module:No globals')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
-- Function allowing for consistent treatment of boolean-like wikitext input.
-- It works similarly to Module:Yesno
local function yesno(val, default)
if type(val) == 'boolean' then
return val
elseif type(val) == 'number' then
if val==1 then
return true
elseif val==0 then
return false
end
elseif type(val) == 'string' then
val = mw.ustring.lower(val) -- put in lower case
if val == 'no' or val == 'n' or val == 'false' or tonumber(val) == 0 then
return false
elseif val == 'yes' or val == 'y' or val == 'true' or tonumber(val) == 1 then
return true
end
end
return default
end
---------------------------------------------------------------------------------------
-- String replacement that ignores part of the string in "..."
local function strReplace(String, old, new)
if String:find('"') then
local T={}
for i, str in ipairs(mw.text.split( String, '"', true )) do
if i%2==1 then
str = str:gsub(old, new)
end
table.insert(T, str)
end
return table.concat(T,'"')
else
return String:gsub(old, new)
end
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datevec - Array of {year,month,day,hour,minute,second, tzhour, tzmin} containing broken
-- down date-time component strings or numbers
-- OUTPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - same array but holding only numbers or nuls
local function parserDatevec(datevec)
-- if month is not a number than check if it is a month name in project's language
local month = datevec[2]
if month and not tonumber(month) then
datevec[2] = mw.getContentLanguage():formatDate( "n", month)
end
-- create datecode based on which variables are provided and check for out-of-bound values
local maxval = {nil, 12, 31, 23, 59, 59, 23, 59} -- max values for year, month, ...
local minval = {nil, 1, 1, 1, 1, 1, -23, 1} -- min values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i = 1,8 do
datenum[i] = tonumber(datevec[i])
if datenum[i] and (i==1 or (datenum[i]>=minval[i] and datenum[i]<=maxval[i])) then
datecode = datecode .. c[i]
end
end
return datecode, datenum
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - Array of {year,month,day,hour,minute,second, tzhour, tzmin} as numbers or nuls
-- OUTPUT:
-- * timeStamp - date string in the format taken by mw.language:formatDate lua function and {{#time}} perser function
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
-- * datecode - with possible corrections
local function getTimestamp(datecode, datenum)
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
else
timeStamp = nil -- format not supported
end
return timeStamp, datecode
end
---------------------------------------------------------------------------------------
-- trim leading zeros in years prior to year 1000
-- INPUT:
-- * datestr - translated date string
-- * lang - language of translation
-- OUTPUT:
-- * datestr - updated date string
local function trimYear(datestr, year, lang)
local yearStr0, yearStr1, yearStr2, zeroStr
yearStr0 = string.format('%04i', year ) -- 4 digit year in standard form "0123"
yearStr1 = mw.language.new(lang):formatDate( 'Y', yearStr0) -- same as calling {{#time}} parser function
--yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', yearStr0, lang } ) -- translate to a language
if yearStr0==yearStr1 then -- most of languages use standard form of year
yearStr2 = tostring(year)
else -- some languages use different characters for numbers
yearStr2 = yearStr1
zeroStr = mw.ustring.sub(yearStr1,1,1) -- get "0" in whatever language
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
end
return string.gsub(datestr, yearStr1, yearStr2 ) -- in datestr replace long year with trimmed one
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * datecode: YMDHMS, YMDHM, YMD, YM, MD, Y, or M
-- * day : Number between 1 and 31 (not needed for most languages)
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function getDateFormat(datecode, day, lang)
local function parseFormat(dFormat, day)
if dFormat:find('default') and #dFormat>10 then
-- special (and messy) case of dFormat code depending on a day number
-- then json contains a string with more json containing "default" field and 2 digit day keys
-- if desired day is not in that json than use "default" case
dFormat = dFormat:gsub('”','"') -- change fancy double quote to a straight one, used for json marking
local D = mw.text.jsonDecode( dFormat ) --com = mw.dumpObject(D)
day = string.format('d%02i',day) -- create day key
dFormat = D[day] or D.default
dFormat = dFormat:gsub("'", '"') -- change single quote to a double quote, used for {{#time}} marking
end
return dFormat
end
local T = {}
local tab = mw.ext.data.get('DateI18n.tab', lang)
for _, row in pairs(tab.data) do -- convert the output into a dictionary table
local id, _, msg = unpack(row)
T[id] = msg
end
local dFormat = T[datecode]
if dFormat=='default' and (datecode=='YMDHMS' or datecode=='YMDHM') then
-- for most languages adding hour:minute:second is done by adding ", HH:MM:SS to the
-- day precission date, those languages are skipped in DateI18n.tab and default to
-- English which stores word "default"
dFormat = parseFormat(T['YMD'], day).. ', H:i'
if datecode=='YMDHMS' then
dFormat = dFormat .. ':s'
end
else
dFormat = parseFormat(dFormat, day)
end
return dFormat
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * month : month number
-- * case : gramatic case abbriviation, like "ins", "loc"
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function MonthCase(month, case, lang)
local T = {{},{},{},{},{},{},{},{},{},{},{},{}}
local tab = mw.ext.data.get('I18n/MonthCases.tab', lang)
for _, row in pairs(tab.data) do
local mth, cs, msg = unpack(row)
T[mth][cs] = msg
end
return T[month][case]
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
local Date = require('Module:DateI18n')._Date
local dateStr = Date({2020, 12, 30, 12, 20, 11}, lang)
Parameters:
* {year,month,day,hour,minute,second, tzhour, tzmin}: broken down date-time component strings or numbers
tzhour, tzmin are timezone offsets from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
if not lang or not mw.language.isValidCode( lang ) then
lang = mw.getCurrentFrame():callParserFunction( "int", "lang" ) -- get user's chosen language
end
if lang == 'be-tarsk' then
lang = 'be-x-old'
end
-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array
local datecode, datenum = parserDatevec(datevec)
local year, month, day = datenum[1], datenum[2], datenum[3]
local timeStamp, datecode = getTimestamp(datecode, datenum)
if not timeStamp then -- something went wrong in parserDatevec
return ''
end
-- Commons [[Data:DateI18n.tab]] page stores prefered formats for diferent
-- languages and datecodes (specifying year-month-day or just year of month-day, etc)
-- Look up country specific format input to {{#time}} function
local dFormat = getDateFormat(datecode, day, lang)
-- By default the gramatical case is not specified (case=='') allowing the format to be specified
-- in [[Data:DateI18n.tab]]. You can overwrite the default grammatical case of the month by
-- specifying "case" variable. This is needed mostly by Slavic languages to create more complex
-- phrases as it is done in [[c:Module:Complex date]]
case = case or ''
if (lang=='qu' or lang=='qug') and (case=='nom') then
-- Special case related to Quechua and Kichwa languages. The form in the I18n is
-- Genitive case with suffix "pi" added to month names provided by {#time}}
-- in Nominative case that "pi" should be removed
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
dFormat = dFormat:gsub('F"pi"', 'F')
elseif (case=='gen') then
dFormat = strReplace(dFormat, "F", "xg")
elseif (case=='nom') then
dFormat = strReplace(dFormat, "xg", "F")
elseif (case ~= '') then
-- see is page [[Data:I18n/MonthCases.tab]] on Commons have name of the month
-- in specific gramatic case in desired language. If we have it than replace
-- "F" and xg" in dFormat
local monthMsg = MonthCase(month, case, lang)
if monthMsg and monthMsg ~= '' then -- make sure it exists
dFormat = strReplace(dFormat, 'F', '"'..monthMsg..'"') -- replace default month with month name we already looked up
dFormat = strReplace(dFormat, 'xg', '"'..monthMsg..'"')
end
end
-- Translate the date using specified format
-- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate and
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions##time for explanation of the format
local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp) -- same as using {{#time}} parser function
-- Special case related to Thai solar calendar: prior to 1940 new-year was at different
-- time of year, so just year (datecode=='Y') is ambiguous and is replaced by "YYYY or YYYY" phrase
if lang=='th' and datecode=='Y' and year<=1940 then
datestr = string.format('%04i หรือ %04i', year+542, year+543 )
end
-- If year<1000 than either keep the date padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if year and year<1000 then
if type(trim_year)=='nil' then
trim_year = '100-999'
end
local trim = yesno(trim_year,nil) -- convert to boolean
if trim==nil and type(trim_year)=='string' then
-- if "trim_year" not a simple True/False than it is range of dates
-- for example '100-999' means to pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and year>=tonumber(YMin) and year<=tonumber(YMax))
end
if trim==true then
datestr = trimYear(datestr, year, lang) -- in datestr replace long year with trimmed one
end
end
-- append timezone if present
if datenum[7] and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
-- use {{#time}} parser function to create timezone string, so that we use correct character set
local sign = (datenum[7]<0) and '−' or '+'
timeStamp = string.format("2000-01-01 %02i:%02i:00", math.abs(datenum[7]), datevec[8] or 0)
local timezone = mw.language.new(lang):formatDate( 'H:i', timeStamp) -- same as using {{#time}} parser function
datestr = string.format("%s %s%s", datestr, sign, timezone )
end
-- html formating and tagging of date string
if class and class ~= '' and datecode~='M' and datecode~='MD'then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
{{#invoke:DateI18n|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
* year, month, day, hour, minute, second: broken down date-time component strings
* tzhour, tzmin: timezone offset from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p.Date(frame)
local args = {}
for name, value in pairs( frame.args ) do
name = string.gsub( string.lower(name), ' ', '_')
args[name] = value
end
return p._Date(
{ args.year, args.month, args.day, args.hour, args.minute, args.second, args.tzhour, args.tzmin },
args.lang, -- language
args.case, -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
return p
tvygiry26r6040u8ipj0ym16hioyicd
803616
803615
2020-04-12T13:31:24Z
commons>Jarekt
0
correction to the last edit
803616
Scribunto
text/plain
--[[
__ __ _ _ ____ _ ___ _ ___
| \/ | ___ __| |_ _| | ___ _| _ \ __ _| |_ ___|_ _/ |( _ ) _ __
| |\/| |/ _ \ / _` | | | | |/ _ (_) | | |/ _` | __/ _ \| || |/ _ \| '_ \
| | | | (_) | (_| | |_| | | __/_| |_| | (_| | || __/| || | (_) | | | |
|_| |_|\___/ \__,_|\__,_|_|\___(_)____/ \__,_|\__\___|___|_|\___/|_| |_|
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date
]]
require('Module:No globals')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
-- Function allowing for consistent treatment of boolean-like wikitext input.
-- It works similarly to Module:Yesno
local function yesno(val, default)
if type(val) == 'boolean' then
return val
elseif type(val) == 'number' then
if val==1 then
return true
elseif val==0 then
return false
end
elseif type(val) == 'string' then
val = mw.ustring.lower(val) -- put in lower case
if val == 'no' or val == 'n' or val == 'false' or tonumber(val) == 0 then
return false
elseif val == 'yes' or val == 'y' or val == 'true' or tonumber(val) == 1 then
return true
end
end
return default
end
---------------------------------------------------------------------------------------
-- String replacement that ignores part of the string in "..."
local function strReplace(String, old, new)
if String:find('"') then
local T={}
for i, str in ipairs(mw.text.split( String, '"', true )) do
if i%2==1 then
str = str:gsub(old, new)
end
table.insert(T, str)
end
return table.concat(T,'"')
else
return String:gsub(old, new)
end
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datevec - Array of {year,month,day,hour,minute,second, tzhour, tzmin} containing broken
-- down date-time component strings or numbers
-- OUTPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - same array but holding only numbers or nuls
local function parserDatevec(datevec)
-- if month is not a number than check if it is a month name in project's language
local month = datevec[2]
if month and not tonumber(month) then
datevec[2] = mw.getContentLanguage():formatDate( "n", month)
end
-- create datecode based on which variables are provided and check for out-of-bound values
local maxval = {nil, 12, 31, 23, 59, 59, 23, 59} -- max values for year, month, ...
local minval = {nil, 1, 0, 0, 0, 0, -23, 0} -- min values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i = 1,8 do
datenum[i] = tonumber(datevec[i])
if datenum[i] and (i==1 or (datenum[i]>=minval[i] and datenum[i]<=maxval[i])) then
datecode = datecode .. c[i]
end
end
return datecode, datenum
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - Array of {year,month,day,hour,minute,second, tzhour, tzmin} as numbers or nuls
-- OUTPUT:
-- * timeStamp - date string in the format taken by mw.language:formatDate lua function and {{#time}} perser function
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
-- * datecode - with possible corrections
local function getTimestamp(datecode, datenum)
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
else
timeStamp = nil -- format not supported
end
return timeStamp, datecode
end
---------------------------------------------------------------------------------------
-- trim leading zeros in years prior to year 1000
-- INPUT:
-- * datestr - translated date string
-- * lang - language of translation
-- OUTPUT:
-- * datestr - updated date string
local function trimYear(datestr, year, lang)
local yearStr0, yearStr1, yearStr2, zeroStr
yearStr0 = string.format('%04i', year ) -- 4 digit year in standard form "0123"
yearStr1 = mw.language.new(lang):formatDate( 'Y', yearStr0) -- same as calling {{#time}} parser function
--yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', yearStr0, lang } ) -- translate to a language
if yearStr0==yearStr1 then -- most of languages use standard form of year
yearStr2 = tostring(year)
else -- some languages use different characters for numbers
yearStr2 = yearStr1
zeroStr = mw.ustring.sub(yearStr1,1,1) -- get "0" in whatever language
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
end
return string.gsub(datestr, yearStr1, yearStr2 ) -- in datestr replace long year with trimmed one
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * datecode: YMDHMS, YMDHM, YMD, YM, MD, Y, or M
-- * day : Number between 1 and 31 (not needed for most languages)
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function getDateFormat(datecode, day, lang)
local function parseFormat(dFormat, day)
if dFormat:find('default') and #dFormat>10 then
-- special (and messy) case of dFormat code depending on a day number
-- then json contains a string with more json containing "default" field and 2 digit day keys
-- if desired day is not in that json than use "default" case
dFormat = dFormat:gsub('”','"') -- change fancy double quote to a straight one, used for json marking
local D = mw.text.jsonDecode( dFormat ) --com = mw.dumpObject(D)
day = string.format('d%02i',day) -- create day key
dFormat = D[day] or D.default
dFormat = dFormat:gsub("'", '"') -- change single quote to a double quote, used for {{#time}} marking
end
return dFormat
end
local T = {}
local tab = mw.ext.data.get('DateI18n.tab', lang)
for _, row in pairs(tab.data) do -- convert the output into a dictionary table
local id, _, msg = unpack(row)
T[id] = msg
end
local dFormat = T[datecode]
if dFormat=='default' and (datecode=='YMDHMS' or datecode=='YMDHM') then
-- for most languages adding hour:minute:second is done by adding ", HH:MM:SS to the
-- day precission date, those languages are skipped in DateI18n.tab and default to
-- English which stores word "default"
dFormat = parseFormat(T['YMD'], day).. ', H:i'
if datecode=='YMDHMS' then
dFormat = dFormat .. ':s'
end
else
dFormat = parseFormat(dFormat, day)
end
return dFormat
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * month : month number
-- * case : gramatic case abbriviation, like "ins", "loc"
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function MonthCase(month, case, lang)
local T = {{},{},{},{},{},{},{},{},{},{},{},{}}
local tab = mw.ext.data.get('I18n/MonthCases.tab', lang)
for _, row in pairs(tab.data) do
local mth, cs, msg = unpack(row)
T[mth][cs] = msg
end
return T[month][case]
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
local Date = require('Module:DateI18n')._Date
local dateStr = Date({2020, 12, 30, 12, 20, 11}, lang)
Parameters:
* {year,month,day,hour,minute,second, tzhour, tzmin}: broken down date-time component strings or numbers
tzhour, tzmin are timezone offsets from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
if not lang or not mw.language.isValidCode( lang ) then
lang = mw.getCurrentFrame():callParserFunction( "int", "lang" ) -- get user's chosen language
end
if lang == 'be-tarsk' then
lang = 'be-x-old'
end
-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array
local datecode, datenum = parserDatevec(datevec)
local year, month, day = datenum[1], datenum[2], datenum[3]
local timeStamp, datecode = getTimestamp(datecode, datenum)
if not timeStamp then -- something went wrong in parserDatevec
return ''
end
-- Commons [[Data:DateI18n.tab]] page stores prefered formats for diferent
-- languages and datecodes (specifying year-month-day or just year of month-day, etc)
-- Look up country specific format input to {{#time}} function
local dFormat = getDateFormat(datecode, day, lang)
-- By default the gramatical case is not specified (case=='') allowing the format to be specified
-- in [[Data:DateI18n.tab]]. You can overwrite the default grammatical case of the month by
-- specifying "case" variable. This is needed mostly by Slavic languages to create more complex
-- phrases as it is done in [[c:Module:Complex date]]
case = case or ''
if (lang=='qu' or lang=='qug') and (case=='nom') then
-- Special case related to Quechua and Kichwa languages. The form in the I18n is
-- Genitive case with suffix "pi" added to month names provided by {#time}}
-- in Nominative case that "pi" should be removed
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
dFormat = dFormat:gsub('F"pi"', 'F')
elseif (case=='gen') then
dFormat = strReplace(dFormat, "F", "xg")
elseif (case=='nom') then
dFormat = strReplace(dFormat, "xg", "F")
elseif (case ~= '') then
-- see is page [[Data:I18n/MonthCases.tab]] on Commons have name of the month
-- in specific gramatic case in desired language. If we have it than replace
-- "F" and xg" in dFormat
local monthMsg = MonthCase(month, case, lang)
if monthMsg and monthMsg ~= '' then -- make sure it exists
dFormat = strReplace(dFormat, 'F', '"'..monthMsg..'"') -- replace default month with month name we already looked up
dFormat = strReplace(dFormat, 'xg', '"'..monthMsg..'"')
end
end
-- Translate the date using specified format
-- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate and
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions##time for explanation of the format
local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp) -- same as using {{#time}} parser function
-- Special case related to Thai solar calendar: prior to 1940 new-year was at different
-- time of year, so just year (datecode=='Y') is ambiguous and is replaced by "YYYY or YYYY" phrase
if lang=='th' and datecode=='Y' and year<=1940 then
datestr = string.format('%04i หรือ %04i', year+542, year+543 )
end
-- If year<1000 than either keep the date padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if year and year<1000 then
if type(trim_year)=='nil' then
trim_year = '100-999'
end
local trim = yesno(trim_year,nil) -- convert to boolean
if trim==nil and type(trim_year)=='string' then
-- if "trim_year" not a simple True/False than it is range of dates
-- for example '100-999' means to pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and year>=tonumber(YMin) and year<=tonumber(YMax))
end
if trim==true then
datestr = trimYear(datestr, year, lang) -- in datestr replace long year with trimmed one
end
end
-- append timezone if present
if datenum[7] and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
-- use {{#time}} parser function to create timezone string, so that we use correct character set
local sign = (datenum[7]<0) and '−' or '+'
timeStamp = string.format("2000-01-01 %02i:%02i:00", math.abs(datenum[7]), datenum[8] or 0)
local timezone = mw.language.new(lang):formatDate( 'H:i', timeStamp) -- same as using {{#time}} parser function
datestr = string.format("%s %s%s", datestr, sign, timezone )
end
-- html formating and tagging of date string
if class and class ~= '' and datecode~='M' and datecode~='MD'then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
{{#invoke:DateI18n|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
* year, month, day, hour, minute, second: broken down date-time component strings
* tzhour, tzmin: timezone offset from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p.Date(frame)
local args = {}
for name, value in pairs( frame.args ) do
name = string.gsub( string.lower(name), ' ', '_')
args[name] = value
end
return p._Date(
{ args.year, args.month, args.day, args.hour, args.minute, args.second, args.tzhour, args.tzmin },
args.lang, -- language
args.case, -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
return p
k5zdhfc2luhh5kdznmwbvpw0doztyr2
803617
803616
2020-04-12T14:51:53Z
commons>Jarekt
0
fix issue where "april" was added to the year only date
803617
Scribunto
text/plain
--[[
__ __ _ _ ____ _ ___ _ ___
| \/ | ___ __| |_ _| | ___ _| _ \ __ _| |_ ___|_ _/ |( _ ) _ __
| |\/| |/ _ \ / _` | | | | |/ _ (_) | | |/ _` | __/ _ \| || |/ _ \| '_ \
| | | | (_) | (_| | |_| | | __/_| |_| | (_| | || __/| || | (_) | | | |
|_| |_|\___/ \__,_|\__,_|_|\___(_)____/ \__,_|\__\___|___|_|\___/|_| |_|
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date
]]
require('Module:No globals')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
-- Function allowing for consistent treatment of boolean-like wikitext input.
-- It works similarly to Module:Yesno
local function yesno(val, default)
if type(val) == 'boolean' then
return val
elseif type(val) == 'number' then
if val==1 then
return true
elseif val==0 then
return false
end
elseif type(val) == 'string' then
val = mw.ustring.lower(val) -- put in lower case
if val == 'no' or val == 'n' or val == 'false' or tonumber(val) == 0 then
return false
elseif val == 'yes' or val == 'y' or val == 'true' or tonumber(val) == 1 then
return true
end
end
return default
end
---------------------------------------------------------------------------------------
-- String replacement that ignores part of the string in "..."
local function strReplace(String, old, new)
if String:find('"') then
local T={}
for i, str in ipairs(mw.text.split( String, '"', true )) do
if i%2==1 then
str = str:gsub(old, new)
end
table.insert(T, str)
end
return table.concat(T,'"')
else
return String:gsub(old, new)
end
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datevec - Array of {year,month,day,hour,minute,second, tzhour, tzmin} containing broken
-- down date-time component strings or numbers
-- OUTPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - same array but holding only numbers or nuls
local function parserDatevec(datevec)
-- if month is not a number than check if it is a month name in project's language
local month = datevec[2]
if month and month~='' and not tonumber(month) then
datevec[2] = mw.getContentLanguage():formatDate( "n", month)
end
-- create datecode based on which variables are provided and check for out-of-bound values
local maxval = {nil, 12, 31, 23, 59, 59, 23, 59} -- max values for year, month, ...
local minval = {nil, 1, 1, 0, 0, 0, -23, 0} -- min values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i = 1,8 do
datenum[i] = tonumber(datevec[i])
if datenum[i] and (i==1 or (datenum[i]>=minval[i] and datenum[i]<=maxval[i])) then
datecode = datecode .. c[i]
end
end
return datecode, datenum
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - Array of {year,month,day,hour,minute,second, tzhour, tzmin} as numbers or nuls
-- OUTPUT:
-- * timeStamp - date string in the format taken by mw.language:formatDate lua function and {{#time}} perser function
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
-- * datecode - with possible corrections
local function getTimestamp(datecode, datenum)
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
else
timeStamp = nil -- format not supported
end
return timeStamp, datecode
end
---------------------------------------------------------------------------------------
-- trim leading zeros in years prior to year 1000
-- INPUT:
-- * datestr - translated date string
-- * lang - language of translation
-- OUTPUT:
-- * datestr - updated date string
local function trimYear(datestr, year, lang)
local yearStr0, yearStr1, yearStr2, zeroStr
yearStr0 = string.format('%04i', year ) -- 4 digit year in standard form "0123"
yearStr1 = mw.language.new(lang):formatDate( 'Y', yearStr0) -- same as calling {{#time}} parser function
--yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', yearStr0, lang } ) -- translate to a language
if yearStr0==yearStr1 then -- most of languages use standard form of year
yearStr2 = tostring(year)
else -- some languages use different characters for numbers
yearStr2 = yearStr1
zeroStr = mw.ustring.sub(yearStr1,1,1) -- get "0" in whatever language
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
end
return string.gsub(datestr, yearStr1, yearStr2 ) -- in datestr replace long year with trimmed one
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * datecode: YMDHMS, YMDHM, YMD, YM, MD, Y, or M
-- * day : Number between 1 and 31 (not needed for most languages)
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function getDateFormat(datecode, day, lang)
local function parseFormat(dFormat, day)
if dFormat:find('default') and #dFormat>10 then
-- special (and messy) case of dFormat code depending on a day number
-- then json contains a string with more json containing "default" field and 2 digit day keys
-- if desired day is not in that json than use "default" case
dFormat = dFormat:gsub('”','"') -- change fancy double quote to a straight one, used for json marking
local D = mw.text.jsonDecode( dFormat ) --com = mw.dumpObject(D)
day = string.format('d%02i',day) -- create day key
dFormat = D[day] or D.default
dFormat = dFormat:gsub("'", '"') -- change single quote to a double quote, used for {{#time}} marking
end
return dFormat
end
local T = {}
local tab = mw.ext.data.get('DateI18n.tab', lang)
for _, row in pairs(tab.data) do -- convert the output into a dictionary table
local id, _, msg = unpack(row)
T[id] = msg
end
local dFormat = T[datecode]
if dFormat=='default' and (datecode=='YMDHMS' or datecode=='YMDHM') then
-- for most languages adding hour:minute:second is done by adding ", HH:MM:SS to the
-- day precission date, those languages are skipped in DateI18n.tab and default to
-- English which stores word "default"
dFormat = parseFormat(T['YMD'], day).. ', H:i'
if datecode=='YMDHMS' then
dFormat = dFormat .. ':s'
end
else
dFormat = parseFormat(dFormat, day)
end
return dFormat
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * month : month number
-- * case : gramatic case abbriviation, like "ins", "loc"
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function MonthCase(month, case, lang)
local T = {{},{},{},{},{},{},{},{},{},{},{},{}}
local tab = mw.ext.data.get('I18n/MonthCases.tab', lang)
for _, row in pairs(tab.data) do
local mth, cs, msg = unpack(row)
T[mth][cs] = msg
end
return T[month][case]
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
local Date = require('Module:DateI18n')._Date
local dateStr = Date({2020, 12, 30, 12, 20, 11}, lang)
Parameters:
* {year,month,day,hour,minute,second, tzhour, tzmin}: broken down date-time component strings or numbers
tzhour, tzmin are timezone offsets from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
if not lang or not mw.language.isValidCode( lang ) then
lang = mw.getCurrentFrame():callParserFunction( "int", "lang" ) -- get user's chosen language
end
if lang == 'be-tarsk' then
lang = 'be-x-old'
end
-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array
local datecode, datenum = parserDatevec(datevec)
local year, month, day = datenum[1], datenum[2], datenum[3]
local timeStamp, datecode = getTimestamp(datecode, datenum)
if not timeStamp then -- something went wrong in parserDatevec
return ''
end
-- Commons [[Data:DateI18n.tab]] page stores prefered formats for diferent
-- languages and datecodes (specifying year-month-day or just year of month-day, etc)
-- Look up country specific format input to {{#time}} function
local dFormat = getDateFormat(datecode, day, lang)
-- By default the gramatical case is not specified (case=='') allowing the format to be specified
-- in [[Data:DateI18n.tab]]. You can overwrite the default grammatical case of the month by
-- specifying "case" variable. This is needed mostly by Slavic languages to create more complex
-- phrases as it is done in [[c:Module:Complex date]]
case = case or ''
if (lang=='qu' or lang=='qug') and (case=='nom') then
-- Special case related to Quechua and Kichwa languages. The form in the I18n is
-- Genitive case with suffix "pi" added to month names provided by {#time}}
-- in Nominative case that "pi" should be removed
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
dFormat = dFormat:gsub('F"pi"', 'F')
elseif (case=='gen') then
dFormat = strReplace(dFormat, "F", "xg")
elseif (case=='nom') then
dFormat = strReplace(dFormat, "xg", "F")
elseif (case ~= '') then
-- see is page [[Data:I18n/MonthCases.tab]] on Commons have name of the month
-- in specific gramatic case in desired language. If we have it than replace
-- "F" and xg" in dFormat
local monthMsg = MonthCase(month, case, lang)
if monthMsg and monthMsg ~= '' then -- make sure it exists
dFormat = strReplace(dFormat, 'F', '"'..monthMsg..'"') -- replace default month with month name we already looked up
dFormat = strReplace(dFormat, 'xg', '"'..monthMsg..'"')
end
end
-- Translate the date using specified format
-- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate and
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions##time for explanation of the format
local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp) -- same as using {{#time}} parser function
-- Special case related to Thai solar calendar: prior to 1940 new-year was at different
-- time of year, so just year (datecode=='Y') is ambiguous and is replaced by "YYYY or YYYY" phrase
if lang=='th' and datecode=='Y' and year<=1940 then
datestr = string.format('%04i หรือ %04i', year+542, year+543 )
end
-- If year<1000 than either keep the date padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if year and year<1000 then
if type(trim_year)=='nil' then
trim_year = '100-999'
end
local trim = yesno(trim_year,nil) -- convert to boolean
if trim==nil and type(trim_year)=='string' then
-- if "trim_year" not a simple True/False than it is range of dates
-- for example '100-999' means to pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and year>=tonumber(YMin) and year<=tonumber(YMax))
end
if trim==true then
datestr = trimYear(datestr, year, lang) -- in datestr replace long year with trimmed one
end
end
-- append timezone if present
if datenum[7] and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
-- use {{#time}} parser function to create timezone string, so that we use correct character set
local sign = (datenum[7]<0) and '−' or '+'
timeStamp = string.format("2000-01-01 %02i:%02i:00", math.abs(datenum[7]), datenum[8] or 0)
local timezone = mw.language.new(lang):formatDate( 'H:i', timeStamp) -- same as using {{#time}} parser function
datestr = string.format("%s %s%s", datestr, sign, timezone )
end
-- html formating and tagging of date string
if class and class ~= '' and datecode~='M' and datecode~='MD'then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
{{#invoke:DateI18n|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
* year, month, day, hour, minute, second: broken down date-time component strings
* tzhour, tzmin: timezone offset from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p.Date(frame)
local args = {}
for name, value in pairs( frame.args ) do
name = string.gsub( string.lower(name), ' ', '_')
args[name] = value
end
return p._Date(
{ args.year, args.month, args.day, args.hour, args.minute, args.second, args.tzhour, args.tzmin },
args.lang, -- language
args.case, -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
return p
ber4mpu06s9vonpspt2y1ttwq6gczp6
803618
803617
2020-04-12T23:27:35Z
commons>Jarekt
0
Protected "[[Module:DateI18n]]": [[Commons:Protection policy|High traffic page]] ([Edit=Allow only template editors and administrators] (indefinite) [Move=Allow only template editors and administrators] (indefinite))
803617
Scribunto
text/plain
--[[
__ __ _ _ ____ _ ___ _ ___
| \/ | ___ __| |_ _| | ___ _| _ \ __ _| |_ ___|_ _/ |( _ ) _ __
| |\/| |/ _ \ / _` | | | | |/ _ (_) | | |/ _` | __/ _ \| || |/ _ \| '_ \
| | | | (_) | (_| | |_| | | __/_| |_| | (_| | || __/| || | (_) | | | |
|_| |_|\___/ \__,_|\__,_|_|\___(_)____/ \__,_|\__\___|___|_|\___/|_| |_|
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date
]]
require('Module:No globals')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
-- Function allowing for consistent treatment of boolean-like wikitext input.
-- It works similarly to Module:Yesno
local function yesno(val, default)
if type(val) == 'boolean' then
return val
elseif type(val) == 'number' then
if val==1 then
return true
elseif val==0 then
return false
end
elseif type(val) == 'string' then
val = mw.ustring.lower(val) -- put in lower case
if val == 'no' or val == 'n' or val == 'false' or tonumber(val) == 0 then
return false
elseif val == 'yes' or val == 'y' or val == 'true' or tonumber(val) == 1 then
return true
end
end
return default
end
---------------------------------------------------------------------------------------
-- String replacement that ignores part of the string in "..."
local function strReplace(String, old, new)
if String:find('"') then
local T={}
for i, str in ipairs(mw.text.split( String, '"', true )) do
if i%2==1 then
str = str:gsub(old, new)
end
table.insert(T, str)
end
return table.concat(T,'"')
else
return String:gsub(old, new)
end
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datevec - Array of {year,month,day,hour,minute,second, tzhour, tzmin} containing broken
-- down date-time component strings or numbers
-- OUTPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - same array but holding only numbers or nuls
local function parserDatevec(datevec)
-- if month is not a number than check if it is a month name in project's language
local month = datevec[2]
if month and month~='' and not tonumber(month) then
datevec[2] = mw.getContentLanguage():formatDate( "n", month)
end
-- create datecode based on which variables are provided and check for out-of-bound values
local maxval = {nil, 12, 31, 23, 59, 59, 23, 59} -- max values for year, month, ...
local minval = {nil, 1, 1, 0, 0, 0, -23, 0} -- min values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i = 1,8 do
datenum[i] = tonumber(datevec[i])
if datenum[i] and (i==1 or (datenum[i]>=minval[i] and datenum[i]<=maxval[i])) then
datecode = datecode .. c[i]
end
end
return datecode, datenum
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - Array of {year,month,day,hour,minute,second, tzhour, tzmin} as numbers or nuls
-- OUTPUT:
-- * timeStamp - date string in the format taken by mw.language:formatDate lua function and {{#time}} perser function
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
-- * datecode - with possible corrections
local function getTimestamp(datecode, datenum)
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
else
timeStamp = nil -- format not supported
end
return timeStamp, datecode
end
---------------------------------------------------------------------------------------
-- trim leading zeros in years prior to year 1000
-- INPUT:
-- * datestr - translated date string
-- * lang - language of translation
-- OUTPUT:
-- * datestr - updated date string
local function trimYear(datestr, year, lang)
local yearStr0, yearStr1, yearStr2, zeroStr
yearStr0 = string.format('%04i', year ) -- 4 digit year in standard form "0123"
yearStr1 = mw.language.new(lang):formatDate( 'Y', yearStr0) -- same as calling {{#time}} parser function
--yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', yearStr0, lang } ) -- translate to a language
if yearStr0==yearStr1 then -- most of languages use standard form of year
yearStr2 = tostring(year)
else -- some languages use different characters for numbers
yearStr2 = yearStr1
zeroStr = mw.ustring.sub(yearStr1,1,1) -- get "0" in whatever language
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
end
return string.gsub(datestr, yearStr1, yearStr2 ) -- in datestr replace long year with trimmed one
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * datecode: YMDHMS, YMDHM, YMD, YM, MD, Y, or M
-- * day : Number between 1 and 31 (not needed for most languages)
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function getDateFormat(datecode, day, lang)
local function parseFormat(dFormat, day)
if dFormat:find('default') and #dFormat>10 then
-- special (and messy) case of dFormat code depending on a day number
-- then json contains a string with more json containing "default" field and 2 digit day keys
-- if desired day is not in that json than use "default" case
dFormat = dFormat:gsub('”','"') -- change fancy double quote to a straight one, used for json marking
local D = mw.text.jsonDecode( dFormat ) --com = mw.dumpObject(D)
day = string.format('d%02i',day) -- create day key
dFormat = D[day] or D.default
dFormat = dFormat:gsub("'", '"') -- change single quote to a double quote, used for {{#time}} marking
end
return dFormat
end
local T = {}
local tab = mw.ext.data.get('DateI18n.tab', lang)
for _, row in pairs(tab.data) do -- convert the output into a dictionary table
local id, _, msg = unpack(row)
T[id] = msg
end
local dFormat = T[datecode]
if dFormat=='default' and (datecode=='YMDHMS' or datecode=='YMDHM') then
-- for most languages adding hour:minute:second is done by adding ", HH:MM:SS to the
-- day precission date, those languages are skipped in DateI18n.tab and default to
-- English which stores word "default"
dFormat = parseFormat(T['YMD'], day).. ', H:i'
if datecode=='YMDHMS' then
dFormat = dFormat .. ':s'
end
else
dFormat = parseFormat(dFormat, day)
end
return dFormat
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * month : month number
-- * case : gramatic case abbriviation, like "ins", "loc"
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function MonthCase(month, case, lang)
local T = {{},{},{},{},{},{},{},{},{},{},{},{}}
local tab = mw.ext.data.get('I18n/MonthCases.tab', lang)
for _, row in pairs(tab.data) do
local mth, cs, msg = unpack(row)
T[mth][cs] = msg
end
return T[month][case]
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
local Date = require('Module:DateI18n')._Date
local dateStr = Date({2020, 12, 30, 12, 20, 11}, lang)
Parameters:
* {year,month,day,hour,minute,second, tzhour, tzmin}: broken down date-time component strings or numbers
tzhour, tzmin are timezone offsets from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
if not lang or not mw.language.isValidCode( lang ) then
lang = mw.getCurrentFrame():callParserFunction( "int", "lang" ) -- get user's chosen language
end
if lang == 'be-tarsk' then
lang = 'be-x-old'
end
-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array
local datecode, datenum = parserDatevec(datevec)
local year, month, day = datenum[1], datenum[2], datenum[3]
local timeStamp, datecode = getTimestamp(datecode, datenum)
if not timeStamp then -- something went wrong in parserDatevec
return ''
end
-- Commons [[Data:DateI18n.tab]] page stores prefered formats for diferent
-- languages and datecodes (specifying year-month-day or just year of month-day, etc)
-- Look up country specific format input to {{#time}} function
local dFormat = getDateFormat(datecode, day, lang)
-- By default the gramatical case is not specified (case=='') allowing the format to be specified
-- in [[Data:DateI18n.tab]]. You can overwrite the default grammatical case of the month by
-- specifying "case" variable. This is needed mostly by Slavic languages to create more complex
-- phrases as it is done in [[c:Module:Complex date]]
case = case or ''
if (lang=='qu' or lang=='qug') and (case=='nom') then
-- Special case related to Quechua and Kichwa languages. The form in the I18n is
-- Genitive case with suffix "pi" added to month names provided by {#time}}
-- in Nominative case that "pi" should be removed
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
dFormat = dFormat:gsub('F"pi"', 'F')
elseif (case=='gen') then
dFormat = strReplace(dFormat, "F", "xg")
elseif (case=='nom') then
dFormat = strReplace(dFormat, "xg", "F")
elseif (case ~= '') then
-- see is page [[Data:I18n/MonthCases.tab]] on Commons have name of the month
-- in specific gramatic case in desired language. If we have it than replace
-- "F" and xg" in dFormat
local monthMsg = MonthCase(month, case, lang)
if monthMsg and monthMsg ~= '' then -- make sure it exists
dFormat = strReplace(dFormat, 'F', '"'..monthMsg..'"') -- replace default month with month name we already looked up
dFormat = strReplace(dFormat, 'xg', '"'..monthMsg..'"')
end
end
-- Translate the date using specified format
-- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate and
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions##time for explanation of the format
local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp) -- same as using {{#time}} parser function
-- Special case related to Thai solar calendar: prior to 1940 new-year was at different
-- time of year, so just year (datecode=='Y') is ambiguous and is replaced by "YYYY or YYYY" phrase
if lang=='th' and datecode=='Y' and year<=1940 then
datestr = string.format('%04i หรือ %04i', year+542, year+543 )
end
-- If year<1000 than either keep the date padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if year and year<1000 then
if type(trim_year)=='nil' then
trim_year = '100-999'
end
local trim = yesno(trim_year,nil) -- convert to boolean
if trim==nil and type(trim_year)=='string' then
-- if "trim_year" not a simple True/False than it is range of dates
-- for example '100-999' means to pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and year>=tonumber(YMin) and year<=tonumber(YMax))
end
if trim==true then
datestr = trimYear(datestr, year, lang) -- in datestr replace long year with trimmed one
end
end
-- append timezone if present
if datenum[7] and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
-- use {{#time}} parser function to create timezone string, so that we use correct character set
local sign = (datenum[7]<0) and '−' or '+'
timeStamp = string.format("2000-01-01 %02i:%02i:00", math.abs(datenum[7]), datenum[8] or 0)
local timezone = mw.language.new(lang):formatDate( 'H:i', timeStamp) -- same as using {{#time}} parser function
datestr = string.format("%s %s%s", datestr, sign, timezone )
end
-- html formating and tagging of date string
if class and class ~= '' and datecode~='M' and datecode~='MD'then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
{{#invoke:DateI18n|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
* year, month, day, hour, minute, second: broken down date-time component strings
* tzhour, tzmin: timezone offset from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p.Date(frame)
local args = {}
for name, value in pairs( frame.args ) do
name = string.gsub( string.lower(name), ' ', '_')
args[name] = value
end
return p._Date(
{ args.year, args.month, args.day, args.hour, args.minute, args.second, args.tzhour, args.tzmin },
args.lang, -- language
args.case, -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
return p
ber4mpu06s9vonpspt2y1ttwq6gczp6
803619
803618
2020-04-30T20:50:47Z
en>RexxS
0
6 revisions imported from [[:commons:Module:DateI18n]]: updating to latest version
803617
Scribunto
text/plain
--[[
__ __ _ _ ____ _ ___ _ ___
| \/ | ___ __| |_ _| | ___ _| _ \ __ _| |_ ___|_ _/ |( _ ) _ __
| |\/| |/ _ \ / _` | | | | |/ _ (_) | | |/ _` | __/ _ \| || |/ _ \| '_ \
| | | | (_) | (_| | |_| | | __/_| |_| | (_| | || __/| || | (_) | | | |
|_| |_|\___/ \__,_|\__,_|_|\___(_)____/ \__,_|\__\___|___|_|\___/|_| |_|
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date
]]
require('Module:No globals')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
-- Function allowing for consistent treatment of boolean-like wikitext input.
-- It works similarly to Module:Yesno
local function yesno(val, default)
if type(val) == 'boolean' then
return val
elseif type(val) == 'number' then
if val==1 then
return true
elseif val==0 then
return false
end
elseif type(val) == 'string' then
val = mw.ustring.lower(val) -- put in lower case
if val == 'no' or val == 'n' or val == 'false' or tonumber(val) == 0 then
return false
elseif val == 'yes' or val == 'y' or val == 'true' or tonumber(val) == 1 then
return true
end
end
return default
end
---------------------------------------------------------------------------------------
-- String replacement that ignores part of the string in "..."
local function strReplace(String, old, new)
if String:find('"') then
local T={}
for i, str in ipairs(mw.text.split( String, '"', true )) do
if i%2==1 then
str = str:gsub(old, new)
end
table.insert(T, str)
end
return table.concat(T,'"')
else
return String:gsub(old, new)
end
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datevec - Array of {year,month,day,hour,minute,second, tzhour, tzmin} containing broken
-- down date-time component strings or numbers
-- OUTPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - same array but holding only numbers or nuls
local function parserDatevec(datevec)
-- if month is not a number than check if it is a month name in project's language
local month = datevec[2]
if month and month~='' and not tonumber(month) then
datevec[2] = mw.getContentLanguage():formatDate( "n", month)
end
-- create datecode based on which variables are provided and check for out-of-bound values
local maxval = {nil, 12, 31, 23, 59, 59, 23, 59} -- max values for year, month, ...
local minval = {nil, 1, 1, 0, 0, 0, -23, 0} -- min values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i = 1,8 do
datenum[i] = tonumber(datevec[i])
if datenum[i] and (i==1 or (datenum[i]>=minval[i] and datenum[i]<=maxval[i])) then
datecode = datecode .. c[i]
end
end
return datecode, datenum
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - Array of {year,month,day,hour,minute,second, tzhour, tzmin} as numbers or nuls
-- OUTPUT:
-- * timeStamp - date string in the format taken by mw.language:formatDate lua function and {{#time}} perser function
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
-- * datecode - with possible corrections
local function getTimestamp(datecode, datenum)
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
else
timeStamp = nil -- format not supported
end
return timeStamp, datecode
end
---------------------------------------------------------------------------------------
-- trim leading zeros in years prior to year 1000
-- INPUT:
-- * datestr - translated date string
-- * lang - language of translation
-- OUTPUT:
-- * datestr - updated date string
local function trimYear(datestr, year, lang)
local yearStr0, yearStr1, yearStr2, zeroStr
yearStr0 = string.format('%04i', year ) -- 4 digit year in standard form "0123"
yearStr1 = mw.language.new(lang):formatDate( 'Y', yearStr0) -- same as calling {{#time}} parser function
--yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', yearStr0, lang } ) -- translate to a language
if yearStr0==yearStr1 then -- most of languages use standard form of year
yearStr2 = tostring(year)
else -- some languages use different characters for numbers
yearStr2 = yearStr1
zeroStr = mw.ustring.sub(yearStr1,1,1) -- get "0" in whatever language
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
end
return string.gsub(datestr, yearStr1, yearStr2 ) -- in datestr replace long year with trimmed one
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * datecode: YMDHMS, YMDHM, YMD, YM, MD, Y, or M
-- * day : Number between 1 and 31 (not needed for most languages)
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function getDateFormat(datecode, day, lang)
local function parseFormat(dFormat, day)
if dFormat:find('default') and #dFormat>10 then
-- special (and messy) case of dFormat code depending on a day number
-- then json contains a string with more json containing "default" field and 2 digit day keys
-- if desired day is not in that json than use "default" case
dFormat = dFormat:gsub('”','"') -- change fancy double quote to a straight one, used for json marking
local D = mw.text.jsonDecode( dFormat ) --com = mw.dumpObject(D)
day = string.format('d%02i',day) -- create day key
dFormat = D[day] or D.default
dFormat = dFormat:gsub("'", '"') -- change single quote to a double quote, used for {{#time}} marking
end
return dFormat
end
local T = {}
local tab = mw.ext.data.get('DateI18n.tab', lang)
for _, row in pairs(tab.data) do -- convert the output into a dictionary table
local id, _, msg = unpack(row)
T[id] = msg
end
local dFormat = T[datecode]
if dFormat=='default' and (datecode=='YMDHMS' or datecode=='YMDHM') then
-- for most languages adding hour:minute:second is done by adding ", HH:MM:SS to the
-- day precission date, those languages are skipped in DateI18n.tab and default to
-- English which stores word "default"
dFormat = parseFormat(T['YMD'], day).. ', H:i'
if datecode=='YMDHMS' then
dFormat = dFormat .. ':s'
end
else
dFormat = parseFormat(dFormat, day)
end
return dFormat
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * month : month number
-- * case : gramatic case abbriviation, like "ins", "loc"
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function MonthCase(month, case, lang)
local T = {{},{},{},{},{},{},{},{},{},{},{},{}}
local tab = mw.ext.data.get('I18n/MonthCases.tab', lang)
for _, row in pairs(tab.data) do
local mth, cs, msg = unpack(row)
T[mth][cs] = msg
end
return T[month][case]
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
local Date = require('Module:DateI18n')._Date
local dateStr = Date({2020, 12, 30, 12, 20, 11}, lang)
Parameters:
* {year,month,day,hour,minute,second, tzhour, tzmin}: broken down date-time component strings or numbers
tzhour, tzmin are timezone offsets from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
if not lang or not mw.language.isValidCode( lang ) then
lang = mw.getCurrentFrame():callParserFunction( "int", "lang" ) -- get user's chosen language
end
if lang == 'be-tarsk' then
lang = 'be-x-old'
end
-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array
local datecode, datenum = parserDatevec(datevec)
local year, month, day = datenum[1], datenum[2], datenum[3]
local timeStamp, datecode = getTimestamp(datecode, datenum)
if not timeStamp then -- something went wrong in parserDatevec
return ''
end
-- Commons [[Data:DateI18n.tab]] page stores prefered formats for diferent
-- languages and datecodes (specifying year-month-day or just year of month-day, etc)
-- Look up country specific format input to {{#time}} function
local dFormat = getDateFormat(datecode, day, lang)
-- By default the gramatical case is not specified (case=='') allowing the format to be specified
-- in [[Data:DateI18n.tab]]. You can overwrite the default grammatical case of the month by
-- specifying "case" variable. This is needed mostly by Slavic languages to create more complex
-- phrases as it is done in [[c:Module:Complex date]]
case = case or ''
if (lang=='qu' or lang=='qug') and (case=='nom') then
-- Special case related to Quechua and Kichwa languages. The form in the I18n is
-- Genitive case with suffix "pi" added to month names provided by {#time}}
-- in Nominative case that "pi" should be removed
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
dFormat = dFormat:gsub('F"pi"', 'F')
elseif (case=='gen') then
dFormat = strReplace(dFormat, "F", "xg")
elseif (case=='nom') then
dFormat = strReplace(dFormat, "xg", "F")
elseif (case ~= '') then
-- see is page [[Data:I18n/MonthCases.tab]] on Commons have name of the month
-- in specific gramatic case in desired language. If we have it than replace
-- "F" and xg" in dFormat
local monthMsg = MonthCase(month, case, lang)
if monthMsg and monthMsg ~= '' then -- make sure it exists
dFormat = strReplace(dFormat, 'F', '"'..monthMsg..'"') -- replace default month with month name we already looked up
dFormat = strReplace(dFormat, 'xg', '"'..monthMsg..'"')
end
end
-- Translate the date using specified format
-- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate and
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions##time for explanation of the format
local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp) -- same as using {{#time}} parser function
-- Special case related to Thai solar calendar: prior to 1940 new-year was at different
-- time of year, so just year (datecode=='Y') is ambiguous and is replaced by "YYYY or YYYY" phrase
if lang=='th' and datecode=='Y' and year<=1940 then
datestr = string.format('%04i หรือ %04i', year+542, year+543 )
end
-- If year<1000 than either keep the date padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if year and year<1000 then
if type(trim_year)=='nil' then
trim_year = '100-999'
end
local trim = yesno(trim_year,nil) -- convert to boolean
if trim==nil and type(trim_year)=='string' then
-- if "trim_year" not a simple True/False than it is range of dates
-- for example '100-999' means to pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and year>=tonumber(YMin) and year<=tonumber(YMax))
end
if trim==true then
datestr = trimYear(datestr, year, lang) -- in datestr replace long year with trimmed one
end
end
-- append timezone if present
if datenum[7] and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
-- use {{#time}} parser function to create timezone string, so that we use correct character set
local sign = (datenum[7]<0) and '−' or '+'
timeStamp = string.format("2000-01-01 %02i:%02i:00", math.abs(datenum[7]), datenum[8] or 0)
local timezone = mw.language.new(lang):formatDate( 'H:i', timeStamp) -- same as using {{#time}} parser function
datestr = string.format("%s %s%s", datestr, sign, timezone )
end
-- html formating and tagging of date string
if class and class ~= '' and datecode~='M' and datecode~='MD'then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
{{#invoke:DateI18n|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
* year, month, day, hour, minute, second: broken down date-time component strings
* tzhour, tzmin: timezone offset from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p.Date(frame)
local args = {}
for name, value in pairs( frame.args ) do
name = string.gsub( string.lower(name), ' ', '_')
args[name] = value
end
return p._Date(
{ args.year, args.month, args.day, args.hour, args.minute, args.second, args.tzhour, args.tzmin },
args.lang, -- language
args.case, -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
return p
ber4mpu06s9vonpspt2y1ttwq6gczp6
803620
803619
2020-04-30T20:54:26Z
en>RexxS
0
Reverted to revision 879901483 by [[Special:Contributions/Ferret|Ferret]] ([[User talk:Ferret|talk]]): Restore last working version ([[WP:TW|TW]])
803620
Scribunto
text/plain
--[[
This module is intended for processing of date strings.
Copied from https://commons.wikimedia.org/w/index.php?title=Module:Date&oldid=224728211
Used by Module:ISOdate, Module:Complex date, Module:WikidataIB
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date and template:ISOyear
]]
local p = {}
-- =======================================
-- === Dependencies ======================
-- =======================================
local i18n = require('Module:I18n/date') -- get localized translations of date formats
local yesno = require('Module:Yesno')
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
for i,language in ipairs(langList) do
if list[language] then
return list[language]
end
end
end
--[[
Date
This function can be used to provide an ISOdate template.
Usage:
{{#invoke:Date|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
year,month,day,hour,minute,second: broken down date-time component strings
tzhour, tzmin: timezone offset from UTC, hours and minutes
lang: The language to display it in
case: Language format (genitive, etc.) for some languages
class: CSS class for the <time> node, use "" for no metadata at all
Error Handling:
]]
function p.Date(frame)
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
return p._Date(
{
args.year or '',
args.month or '',
args.day or '',
args.hour or '',
args.minute or '',
args.second or '',
args.tzhour or '',
args.tzmin or ''
},
args.lang, -- language
args.case or '', -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
for i = #datevec + 1, 8 do
datevec[i] = ''
end
if not case then case = '' end
if not class then class = '' end
if not trim_year then trim_year = '100-999' end
-- if language is not provided than look up users language
-- WARNING: This step should be done by the template as it does not seem to work as well here (cache issues?)
if not lang or not mw.language.isValidCode( lang ) then
lang = 'en'
end
-- Just in case someone broke the internationalization code than fix the english defaults
if i18n.DateLang['en'] == nil then
i18n.DateLang['en'] = 'en-form'
end
if i18n.DateFormat['en-form'] == nil then
i18n.DateFormat['en-form'] = {YMDHMS='j F Y, H:i:s', YMDHM='j F Y, H:i', YMD='j F Y', YM='F Y', MD='j F', Y='Y'}
end
-- create datecode based on which variables are provided and check for out of bound values
local maxval = {9999, 12, 31, 23, 59, 60, 23, 59} -- max values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i, v in ipairs( datevec ) do
if v~=nil and v~='' then
datecode = datecode .. c[i]
datenum[i] = tonumber(v)
if datenum[i]==nil and i==2 then
-- month is not a number -> check if it is a month name in English
v = mw.language.new('en'):formatDate( "n", v)
datenum[i] = tonumber(v)
end
if datenum[i]==nil or datenum[i]>maxval[i] then
-- Some numbers are out of range -> abort and return the empty string
return ''
end
end
end
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
class = '' -- date not complete -> no html formating or micro-tagging of date string
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
class = '' -- date not complete -> no html formating or micro-tagging of date string
else
return '' -- format not supported
end
-- ==========================================================
-- === Create Date String using in chosen language
-- ==========================================================
-- which form should the date take?
-- Use langSwitch to pick formating for each language
local langDateForm = langSwitch(i18n.DateLang, lang)
-- special case of French and Gallic dates, which require different date format for the 1st day of the month
if datenum[3]==1 and (langDateForm=='fr-form' or langDateForm=='ga-form') then
langDateForm = langDateForm .. '1' -- ordinal form for the first day of the month
end
-- special case of Basque dates, which require different date format for the 1st, 11th, 21st and 31st day of the month
if langDateForm=='eu-form' then
if (datenum[3]==1 or datenum[3]==21) then
langDateForm = 'eu-form01'
elseif (datenum[3]==11 or datenum[3]==31) then
langDateForm = 'eu-form11'
end
end
-- Look up country specific format input to {{#time}} function
local dFormat = i18n.DateFormat[langDateForm][datecode]
-- overwrite default grammatical case of the month (applies mostly to Slavic languages)
if (case=='gen') then
-- CAUTION: at the moment i18n.DateFormat uses "F" only as month name, but this might change and this operation does not check if 'F' is in "" brackets or not, so if some language starts using 'F' in "" than this will not work for that language
dFormat = dFormat:gsub("F", "xg");
end
if (case=='nom') then
-- CAUTION: at the moment i18n.DateFormat uses "xg" only as month name, but this might change and this operation does not check if 'xg' is in "" brackets or not, so if some language starts using 'xg' in "" than this will not work for that language
dFormat = dFormat:gsub("xg", "F");
end
if ((lang=='ru' or lang=='pl' or lang=='cs' or lang=='sl' or lang=='sk') and (case=='loc' or case=='ins')) or
(lang=='fi' and (case=='ptv' or case=='ine'or case=='ela'or case=='ill') ) then
local monthEn = mw.language.new('en'):formatDate( "F", timeStamp) -- month name in English
-- month name using proper case and language. It relies on messages stored in MediaWiki namespace for some cases and languages
-- That is why this IF statement uses "lang" not "langDateForm" variable to decide
local monthMsg = mw.message.new( string.format('%s-%s', monthEn, case ) ):inLanguage( lang )
if not monthMsg:isDisabled() then -- make sure it exists
local month=monthMsg:plain()
dFormat = dFormat:gsub('F', '"'..month..'"'); -- replace default month with month name we already looked up
dFormat = dFormat:gsub('xg', '"'..month..'"');
end
end
-- Special case related to Quechua and Kichwa languages
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
if (lang=='qu' or lang=='qug') and case=='nom' then
dFormat = dFormat:gsub('F"pi"', 'F');
end
-- Lua only date formating using {{#time}} parser function (new)
-- prefered call which gives "Lua error: too many language codes requested." on the [[Module talk:Date/sandbox/testcases]] page
--local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp)
local datestr = mw.getCurrentFrame():callParserFunction( "#time", { dFormat, timeStamp, lang } )
-- Another special case related to Thai solar calendar
if lang=='th' and datenum[1]~= nil and datenum[1]<=1940 then
-- As of 2014 {{#time}} parser function did not resolve those cases properly
-- See https://en.wikipedia.org/wiki/Thai_solar_calendar#New_year for reference
-- Disable once https://bugzilla.wikimedia.org/show_bug.cgi?id=66648 is fixed
if datecode=='Y' then -- date is ambiguous
datestr = string.format('%04i หรือ %04i', datenum[1]+542, datenum[1]+543 )
elseif datenum[2]<=3 then -- year is wrong (one too many)
datestr = datestr:gsub( string.format('%04i', datenum[1]+543), string.format('%04i', datenum[1]+542 ) )
end
end
-- If year<1000 than either keep it padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if datenum[1]~= nil and datenum[1]<1000 then
local trim = yesno(trim_year,nil)
if trim == nil then
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and datenum[1]>=tonumber(YMin) and datenum[1]<=tonumber(YMax))
end
-- If the date form isn't the Thai solar calendar, don't zero pad years in the range of 100-999.
-- If at some point support for Islamic/Hebrew/Japanese years is added, they may need to be skipped as well.
if trim then
--local yearStr1 = mw.language.new(lang):formatDate( 'Y', timeStamp)
local yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', timeStamp, lang } )
--local yearStr1 = datestr:match( '%d%d%d%d' ) -- 4 digits in a row (in any language) - that must be a year
local yearStr2 = yearStr1
local zeroStr = mw.ustring.sub(yearStr1,1,1)
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
datestr = datestr:gsub( yearStr1, yearStr2 )
--datestr = string.format('%s (%s, %s)', datestr, yearStr1, yearStr2 )
end
end
-- append timezone if present
if datevec[7] ~= '' and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
local tzstr, tzhournum = '', tonumber(datevec[7])
if tzhournum < 0 then tzstr = '−' else tzstr = '+' end
tzstr = tzstr..string.format("%02d", math.abs(tzhournum))..':'
if datevec[8] ~= '' then tzstr = tzstr..datevec[8] else tzstr = tzstr..'00' end
datestr = datestr..' '..tzstr
end
-- html formating and tagging of date string
if class ~= '' then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
return p
2qhdyl056nbwaexn38ogtoc8cvnzot8
803621
803620
2020-05-14T21:05:37Z
en>RexxS
0
Undid revision 954131094 by [[Special:Contributions/RexxS|RexxS]] ([[User talk:RexxS|talk]]) errors should be fixed (sandboxes working) - try new version again
803621
Scribunto
text/plain
--[[
__ __ _ _ ____ _ ___ _ ___
| \/ | ___ __| |_ _| | ___ _| _ \ __ _| |_ ___|_ _/ |( _ ) _ __
| |\/| |/ _ \ / _` | | | | |/ _ (_) | | |/ _` | __/ _ \| || |/ _ \| '_ \
| | | | (_) | (_| | |_| | | __/_| |_| | (_| | || __/| || | (_) | | | |
|_| |_|\___/ \__,_|\__,_|_|\___(_)____/ \__,_|\__\___|___|_|\___/|_| |_|
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date
]]
require('Module:No globals')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
-- Function allowing for consistent treatment of boolean-like wikitext input.
-- It works similarly to Module:Yesno
local function yesno(val, default)
if type(val) == 'boolean' then
return val
elseif type(val) == 'number' then
if val==1 then
return true
elseif val==0 then
return false
end
elseif type(val) == 'string' then
val = mw.ustring.lower(val) -- put in lower case
if val == 'no' or val == 'n' or val == 'false' or tonumber(val) == 0 then
return false
elseif val == 'yes' or val == 'y' or val == 'true' or tonumber(val) == 1 then
return true
end
end
return default
end
---------------------------------------------------------------------------------------
-- String replacement that ignores part of the string in "..."
local function strReplace(String, old, new)
if String:find('"') then
local T={}
for i, str in ipairs(mw.text.split( String, '"', true )) do
if i%2==1 then
str = str:gsub(old, new)
end
table.insert(T, str)
end
return table.concat(T,'"')
else
return String:gsub(old, new)
end
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datevec - Array of {year,month,day,hour,minute,second, tzhour, tzmin} containing broken
-- down date-time component strings or numbers
-- OUTPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - same array but holding only numbers or nuls
local function parserDatevec(datevec)
-- if month is not a number than check if it is a month name in project's language
local month = datevec[2]
if month and month~='' and not tonumber(month) then
datevec[2] = mw.getContentLanguage():formatDate( "n", month)
end
-- create datecode based on which variables are provided and check for out-of-bound values
local maxval = {nil, 12, 31, 23, 59, 59, 23, 59} -- max values for year, month, ...
local minval = {nil, 1, 1, 0, 0, 0, -23, 0} -- min values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i = 1,8 do
datenum[i] = tonumber(datevec[i])
if datenum[i] and (i==1 or (datenum[i]>=minval[i] and datenum[i]<=maxval[i])) then
datecode = datecode .. c[i]
end
end
return datecode, datenum
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - Array of {year,month,day,hour,minute,second, tzhour, tzmin} as numbers or nuls
-- OUTPUT:
-- * timeStamp - date string in the format taken by mw.language:formatDate lua function and {{#time}} perser function
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
-- * datecode - with possible corrections
local function getTimestamp(datecode, datenum)
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
else
timeStamp = nil -- format not supported
end
return timeStamp, datecode
end
---------------------------------------------------------------------------------------
-- trim leading zeros in years prior to year 1000
-- INPUT:
-- * datestr - translated date string
-- * lang - language of translation
-- OUTPUT:
-- * datestr - updated date string
local function trimYear(datestr, year, lang)
local yearStr0, yearStr1, yearStr2, zeroStr
yearStr0 = string.format('%04i', year ) -- 4 digit year in standard form "0123"
yearStr1 = mw.language.new(lang):formatDate( 'Y', yearStr0) -- same as calling {{#time}} parser function
--yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', yearStr0, lang } ) -- translate to a language
if yearStr0==yearStr1 then -- most of languages use standard form of year
yearStr2 = tostring(year)
else -- some languages use different characters for numbers
yearStr2 = yearStr1
zeroStr = mw.ustring.sub(yearStr1,1,1) -- get "0" in whatever language
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
end
return string.gsub(datestr, yearStr1, yearStr2 ) -- in datestr replace long year with trimmed one
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * datecode: YMDHMS, YMDHM, YMD, YM, MD, Y, or M
-- * day : Number between 1 and 31 (not needed for most languages)
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function getDateFormat(datecode, day, lang)
local function parseFormat(dFormat, day)
if dFormat:find('default') and #dFormat>10 then
-- special (and messy) case of dFormat code depending on a day number
-- then json contains a string with more json containing "default" field and 2 digit day keys
-- if desired day is not in that json than use "default" case
dFormat = dFormat:gsub('”','"') -- change fancy double quote to a straight one, used for json marking
local D = mw.text.jsonDecode( dFormat ) --com = mw.dumpObject(D)
day = string.format('d%02i',day) -- create day key
dFormat = D[day] or D.default
dFormat = dFormat:gsub("'", '"') -- change single quote to a double quote, used for {{#time}} marking
end
return dFormat
end
local T = {}
local tab = mw.ext.data.get('DateI18n.tab', lang)
for _, row in pairs(tab.data) do -- convert the output into a dictionary table
local id, _, msg = unpack(row)
T[id] = msg
end
local dFormat = T[datecode]
if dFormat=='default' and (datecode=='YMDHMS' or datecode=='YMDHM') then
-- for most languages adding hour:minute:second is done by adding ", HH:MM:SS to the
-- day precission date, those languages are skipped in DateI18n.tab and default to
-- English which stores word "default"
dFormat = parseFormat(T['YMD'], day).. ', H:i'
if datecode=='YMDHMS' then
dFormat = dFormat .. ':s'
end
else
dFormat = parseFormat(dFormat, day)
end
return dFormat
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * month : month number
-- * case : gramatic case abbriviation, like "ins", "loc"
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function MonthCase(month, case, lang)
local T = {{},{},{},{},{},{},{},{},{},{},{},{}}
local tab = mw.ext.data.get('I18n/MonthCases.tab', lang)
for _, row in pairs(tab.data) do
local mth, cs, msg = unpack(row)
T[mth][cs] = msg
end
return T[month][case]
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
local Date = require('Module:DateI18n')._Date
local dateStr = Date({2020, 12, 30, 12, 20, 11}, lang)
Parameters:
* {year,month,day,hour,minute,second, tzhour, tzmin}: broken down date-time component strings or numbers
tzhour, tzmin are timezone offsets from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
if not lang or not mw.language.isValidCode( lang ) then
lang = mw.getCurrentFrame():callParserFunction( "int", "lang" ) -- get user's chosen language
end
if lang == 'be-tarsk' then
lang = 'be-x-old'
end
-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array
local datecode, datenum = parserDatevec(datevec)
local year, month, day = datenum[1], datenum[2], datenum[3]
local timeStamp, datecode = getTimestamp(datecode, datenum)
if not timeStamp then -- something went wrong in parserDatevec
return ''
end
-- Commons [[Data:DateI18n.tab]] page stores prefered formats for diferent
-- languages and datecodes (specifying year-month-day or just year of month-day, etc)
-- Look up country specific format input to {{#time}} function
local dFormat = getDateFormat(datecode, day, lang)
-- By default the gramatical case is not specified (case=='') allowing the format to be specified
-- in [[Data:DateI18n.tab]]. You can overwrite the default grammatical case of the month by
-- specifying "case" variable. This is needed mostly by Slavic languages to create more complex
-- phrases as it is done in [[c:Module:Complex date]]
case = case or ''
if (lang=='qu' or lang=='qug') and (case=='nom') then
-- Special case related to Quechua and Kichwa languages. The form in the I18n is
-- Genitive case with suffix "pi" added to month names provided by {#time}}
-- in Nominative case that "pi" should be removed
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
dFormat = dFormat:gsub('F"pi"', 'F')
elseif (case=='gen') then
dFormat = strReplace(dFormat, "F", "xg")
elseif (case=='nom') then
dFormat = strReplace(dFormat, "xg", "F")
elseif (case ~= '') then
-- see is page [[Data:I18n/MonthCases.tab]] on Commons have name of the month
-- in specific gramatic case in desired language. If we have it than replace
-- "F" and xg" in dFormat
local monthMsg = MonthCase(month, case, lang)
if monthMsg and monthMsg ~= '' then -- make sure it exists
dFormat = strReplace(dFormat, 'F', '"'..monthMsg..'"') -- replace default month with month name we already looked up
dFormat = strReplace(dFormat, 'xg', '"'..monthMsg..'"')
end
end
-- Translate the date using specified format
-- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate and
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions##time for explanation of the format
local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp) -- same as using {{#time}} parser function
-- Special case related to Thai solar calendar: prior to 1940 new-year was at different
-- time of year, so just year (datecode=='Y') is ambiguous and is replaced by "YYYY or YYYY" phrase
if lang=='th' and datecode=='Y' and year<=1940 then
datestr = string.format('%04i หรือ %04i', year+542, year+543 )
end
-- If year<1000 than either keep the date padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if year and year<1000 then
if type(trim_year)=='nil' then
trim_year = '100-999'
end
local trim = yesno(trim_year,nil) -- convert to boolean
if trim==nil and type(trim_year)=='string' then
-- if "trim_year" not a simple True/False than it is range of dates
-- for example '100-999' means to pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and year>=tonumber(YMin) and year<=tonumber(YMax))
end
if trim==true then
datestr = trimYear(datestr, year, lang) -- in datestr replace long year with trimmed one
end
end
-- append timezone if present
if datenum[7] and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
-- use {{#time}} parser function to create timezone string, so that we use correct character set
local sign = (datenum[7]<0) and '−' or '+'
timeStamp = string.format("2000-01-01 %02i:%02i:00", math.abs(datenum[7]), datenum[8] or 0)
local timezone = mw.language.new(lang):formatDate( 'H:i', timeStamp) -- same as using {{#time}} parser function
datestr = string.format("%s %s%s", datestr, sign, timezone )
end
-- html formating and tagging of date string
if class and class ~= '' and datecode~='M' and datecode~='MD'then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
{{#invoke:DateI18n|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
* year, month, day, hour, minute, second: broken down date-time component strings
* tzhour, tzmin: timezone offset from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p.Date(frame)
local args = {}
for name, value in pairs( frame.args ) do
name = string.gsub( string.lower(name), ' ', '_')
args[name] = value
end
return p._Date(
{ args.year, args.month, args.day, args.hour, args.minute, args.second, args.tzhour, args.tzmin },
args.lang, -- language
args.case, -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
return p
ber4mpu06s9vonpspt2y1ttwq6gczp6
803622
803621
2022-10-21T06:45:00Z
en>Legoktm
0
Replace [[Module:No globals]] with require( "strict" )
803622
Scribunto
text/plain
--[[
__ __ _ _ ____ _ ___ _ ___
| \/ | ___ __| |_ _| | ___ _| _ \ __ _| |_ ___|_ _/ |( _ ) _ __
| |\/| |/ _ \ / _` | | | | |/ _ (_) | | |/ _` | __/ _ \| || |/ _ \| '_ \
| | | | (_) | (_| | |_| | | __/_| |_| | (_| | || __/| || | (_) | | | |
|_| |_|\___/ \__,_|\__,_|_|\___(_)____/ \__,_|\__\___|___|_|\___/|_| |_|
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date
]]
require('strict')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
-- Function allowing for consistent treatment of boolean-like wikitext input.
-- It works similarly to Module:Yesno
local function yesno(val, default)
if type(val) == 'boolean' then
return val
elseif type(val) == 'number' then
if val==1 then
return true
elseif val==0 then
return false
end
elseif type(val) == 'string' then
val = mw.ustring.lower(val) -- put in lower case
if val == 'no' or val == 'n' or val == 'false' or tonumber(val) == 0 then
return false
elseif val == 'yes' or val == 'y' or val == 'true' or tonumber(val) == 1 then
return true
end
end
return default
end
---------------------------------------------------------------------------------------
-- String replacement that ignores part of the string in "..."
local function strReplace(String, old, new)
if String:find('"') then
local T={}
for i, str in ipairs(mw.text.split( String, '"', true )) do
if i%2==1 then
str = str:gsub(old, new)
end
table.insert(T, str)
end
return table.concat(T,'"')
else
return String:gsub(old, new)
end
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datevec - Array of {year,month,day,hour,minute,second, tzhour, tzmin} containing broken
-- down date-time component strings or numbers
-- OUTPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - same array but holding only numbers or nuls
local function parserDatevec(datevec)
-- if month is not a number than check if it is a month name in project's language
local month = datevec[2]
if month and month~='' and not tonumber(month) then
datevec[2] = mw.getContentLanguage():formatDate( "n", month)
end
-- create datecode based on which variables are provided and check for out-of-bound values
local maxval = {nil, 12, 31, 23, 59, 59, 23, 59} -- max values for year, month, ...
local minval = {nil, 1, 1, 0, 0, 0, -23, 0} -- min values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i = 1,8 do
datenum[i] = tonumber(datevec[i])
if datenum[i] and (i==1 or (datenum[i]>=minval[i] and datenum[i]<=maxval[i])) then
datecode = datecode .. c[i]
end
end
return datecode, datenum
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - Array of {year,month,day,hour,minute,second, tzhour, tzmin} as numbers or nuls
-- OUTPUT:
-- * timeStamp - date string in the format taken by mw.language:formatDate lua function and {{#time}} perser function
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
-- * datecode - with possible corrections
local function getTimestamp(datecode, datenum)
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
else
timeStamp = nil -- format not supported
end
return timeStamp, datecode
end
---------------------------------------------------------------------------------------
-- trim leading zeros in years prior to year 1000
-- INPUT:
-- * datestr - translated date string
-- * lang - language of translation
-- OUTPUT:
-- * datestr - updated date string
local function trimYear(datestr, year, lang)
local yearStr0, yearStr1, yearStr2, zeroStr
yearStr0 = string.format('%04i', year ) -- 4 digit year in standard form "0123"
yearStr1 = mw.language.new(lang):formatDate( 'Y', yearStr0) -- same as calling {{#time}} parser function
--yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', yearStr0, lang } ) -- translate to a language
if yearStr0==yearStr1 then -- most of languages use standard form of year
yearStr2 = tostring(year)
else -- some languages use different characters for numbers
yearStr2 = yearStr1
zeroStr = mw.ustring.sub(yearStr1,1,1) -- get "0" in whatever language
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
end
return string.gsub(datestr, yearStr1, yearStr2 ) -- in datestr replace long year with trimmed one
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * datecode: YMDHMS, YMDHM, YMD, YM, MD, Y, or M
-- * day : Number between 1 and 31 (not needed for most languages)
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function getDateFormat(datecode, day, lang)
local function parseFormat(dFormat, day)
if dFormat:find('default') and #dFormat>10 then
-- special (and messy) case of dFormat code depending on a day number
-- then json contains a string with more json containing "default" field and 2 digit day keys
-- if desired day is not in that json than use "default" case
dFormat = dFormat:gsub('”','"') -- change fancy double quote to a straight one, used for json marking
local D = mw.text.jsonDecode( dFormat ) --com = mw.dumpObject(D)
day = string.format('d%02i',day) -- create day key
dFormat = D[day] or D.default
dFormat = dFormat:gsub("'", '"') -- change single quote to a double quote, used for {{#time}} marking
end
return dFormat
end
local T = {}
local tab = mw.ext.data.get('DateI18n.tab', lang)
for _, row in pairs(tab.data) do -- convert the output into a dictionary table
local id, _, msg = unpack(row)
T[id] = msg
end
local dFormat = T[datecode]
if dFormat=='default' and (datecode=='YMDHMS' or datecode=='YMDHM') then
-- for most languages adding hour:minute:second is done by adding ", HH:MM:SS to the
-- day precission date, those languages are skipped in DateI18n.tab and default to
-- English which stores word "default"
dFormat = parseFormat(T['YMD'], day).. ', H:i'
if datecode=='YMDHMS' then
dFormat = dFormat .. ':s'
end
else
dFormat = parseFormat(dFormat, day)
end
return dFormat
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * month : month number
-- * case : gramatic case abbriviation, like "ins", "loc"
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function MonthCase(month, case, lang)
local T = {{},{},{},{},{},{},{},{},{},{},{},{}}
local tab = mw.ext.data.get('I18n/MonthCases.tab', lang)
for _, row in pairs(tab.data) do
local mth, cs, msg = unpack(row)
T[mth][cs] = msg
end
return T[month][case]
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
local Date = require('Module:DateI18n')._Date
local dateStr = Date({2020, 12, 30, 12, 20, 11}, lang)
Parameters:
* {year,month,day,hour,minute,second, tzhour, tzmin}: broken down date-time component strings or numbers
tzhour, tzmin are timezone offsets from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
if not lang or not mw.language.isValidCode( lang ) then
lang = mw.getCurrentFrame():callParserFunction( "int", "lang" ) -- get user's chosen language
end
if lang == 'be-tarsk' then
lang = 'be-x-old'
end
-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array
local datecode, datenum = parserDatevec(datevec)
local year, month, day = datenum[1], datenum[2], datenum[3]
local timeStamp, datecode = getTimestamp(datecode, datenum)
if not timeStamp then -- something went wrong in parserDatevec
return ''
end
-- Commons [[Data:DateI18n.tab]] page stores prefered formats for diferent
-- languages and datecodes (specifying year-month-day or just year of month-day, etc)
-- Look up country specific format input to {{#time}} function
local dFormat = getDateFormat(datecode, day, lang)
-- By default the gramatical case is not specified (case=='') allowing the format to be specified
-- in [[Data:DateI18n.tab]]. You can overwrite the default grammatical case of the month by
-- specifying "case" variable. This is needed mostly by Slavic languages to create more complex
-- phrases as it is done in [[c:Module:Complex date]]
case = case or ''
if (lang=='qu' or lang=='qug') and (case=='nom') then
-- Special case related to Quechua and Kichwa languages. The form in the I18n is
-- Genitive case with suffix "pi" added to month names provided by {#time}}
-- in Nominative case that "pi" should be removed
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
dFormat = dFormat:gsub('F"pi"', 'F')
elseif (case=='gen') then
dFormat = strReplace(dFormat, "F", "xg")
elseif (case=='nom') then
dFormat = strReplace(dFormat, "xg", "F")
elseif (case ~= '') then
-- see is page [[Data:I18n/MonthCases.tab]] on Commons have name of the month
-- in specific gramatic case in desired language. If we have it than replace
-- "F" and xg" in dFormat
local monthMsg = MonthCase(month, case, lang)
if monthMsg and monthMsg ~= '' then -- make sure it exists
dFormat = strReplace(dFormat, 'F', '"'..monthMsg..'"') -- replace default month with month name we already looked up
dFormat = strReplace(dFormat, 'xg', '"'..monthMsg..'"')
end
end
-- Translate the date using specified format
-- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate and
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions##time for explanation of the format
local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp) -- same as using {{#time}} parser function
-- Special case related to Thai solar calendar: prior to 1940 new-year was at different
-- time of year, so just year (datecode=='Y') is ambiguous and is replaced by "YYYY or YYYY" phrase
if lang=='th' and datecode=='Y' and year<=1940 then
datestr = string.format('%04i หรือ %04i', year+542, year+543 )
end
-- If year<1000 than either keep the date padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if year and year<1000 then
if type(trim_year)=='nil' then
trim_year = '100-999'
end
local trim = yesno(trim_year,nil) -- convert to boolean
if trim==nil and type(trim_year)=='string' then
-- if "trim_year" not a simple True/False than it is range of dates
-- for example '100-999' means to pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and year>=tonumber(YMin) and year<=tonumber(YMax))
end
if trim==true then
datestr = trimYear(datestr, year, lang) -- in datestr replace long year with trimmed one
end
end
-- append timezone if present
if datenum[7] and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
-- use {{#time}} parser function to create timezone string, so that we use correct character set
local sign = (datenum[7]<0) and '−' or '+'
timeStamp = string.format("2000-01-01 %02i:%02i:00", math.abs(datenum[7]), datenum[8] or 0)
local timezone = mw.language.new(lang):formatDate( 'H:i', timeStamp) -- same as using {{#time}} parser function
datestr = string.format("%s %s%s", datestr, sign, timezone )
end
-- html formating and tagging of date string
if class and class ~= '' and datecode~='M' and datecode~='MD'then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
{{#invoke:DateI18n|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
* year, month, day, hour, minute, second: broken down date-time component strings
* tzhour, tzmin: timezone offset from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p.Date(frame)
local args = {}
for name, value in pairs( frame.args ) do
name = string.gsub( string.lower(name), ' ', '_')
args[name] = value
end
return p._Date(
{ args.year, args.month, args.day, args.hour, args.minute, args.second, args.tzhour, args.tzmin },
args.lang, -- language
args.case, -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
return p
lcwmkh5p6iicle9dw7qzn6d8j6xkduv
803623
803622
2024-04-29T16:20:41Z
en>Pppery
0
Protected "[[Module:DateI18n]]" ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
803622
Scribunto
text/plain
--[[
__ __ _ _ ____ _ ___ _ ___
| \/ | ___ __| |_ _| | ___ _| _ \ __ _| |_ ___|_ _/ |( _ ) _ __
| |\/| |/ _ \ / _` | | | | |/ _ (_) | | |/ _` | __/ _ \| || |/ _ \| '_ \
| | | | (_) | (_| | |_| | | __/_| |_| | (_| | || __/| || | (_) | | | |
|_| |_|\___/ \__,_|\__,_|_|\___(_)____/ \__,_|\__\___|___|_|\___/|_| |_|
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date
]]
require('strict')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
-- Function allowing for consistent treatment of boolean-like wikitext input.
-- It works similarly to Module:Yesno
local function yesno(val, default)
if type(val) == 'boolean' then
return val
elseif type(val) == 'number' then
if val==1 then
return true
elseif val==0 then
return false
end
elseif type(val) == 'string' then
val = mw.ustring.lower(val) -- put in lower case
if val == 'no' or val == 'n' or val == 'false' or tonumber(val) == 0 then
return false
elseif val == 'yes' or val == 'y' or val == 'true' or tonumber(val) == 1 then
return true
end
end
return default
end
---------------------------------------------------------------------------------------
-- String replacement that ignores part of the string in "..."
local function strReplace(String, old, new)
if String:find('"') then
local T={}
for i, str in ipairs(mw.text.split( String, '"', true )) do
if i%2==1 then
str = str:gsub(old, new)
end
table.insert(T, str)
end
return table.concat(T,'"')
else
return String:gsub(old, new)
end
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datevec - Array of {year,month,day,hour,minute,second, tzhour, tzmin} containing broken
-- down date-time component strings or numbers
-- OUTPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - same array but holding only numbers or nuls
local function parserDatevec(datevec)
-- if month is not a number than check if it is a month name in project's language
local month = datevec[2]
if month and month~='' and not tonumber(month) then
datevec[2] = mw.getContentLanguage():formatDate( "n", month)
end
-- create datecode based on which variables are provided and check for out-of-bound values
local maxval = {nil, 12, 31, 23, 59, 59, 23, 59} -- max values for year, month, ...
local minval = {nil, 1, 1, 0, 0, 0, -23, 0} -- min values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i = 1,8 do
datenum[i] = tonumber(datevec[i])
if datenum[i] and (i==1 or (datenum[i]>=minval[i] and datenum[i]<=maxval[i])) then
datecode = datecode .. c[i]
end
end
return datecode, datenum
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - Array of {year,month,day,hour,minute,second, tzhour, tzmin} as numbers or nuls
-- OUTPUT:
-- * timeStamp - date string in the format taken by mw.language:formatDate lua function and {{#time}} perser function
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
-- * datecode - with possible corrections
local function getTimestamp(datecode, datenum)
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
else
timeStamp = nil -- format not supported
end
return timeStamp, datecode
end
---------------------------------------------------------------------------------------
-- trim leading zeros in years prior to year 1000
-- INPUT:
-- * datestr - translated date string
-- * lang - language of translation
-- OUTPUT:
-- * datestr - updated date string
local function trimYear(datestr, year, lang)
local yearStr0, yearStr1, yearStr2, zeroStr
yearStr0 = string.format('%04i', year ) -- 4 digit year in standard form "0123"
yearStr1 = mw.language.new(lang):formatDate( 'Y', yearStr0) -- same as calling {{#time}} parser function
--yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', yearStr0, lang } ) -- translate to a language
if yearStr0==yearStr1 then -- most of languages use standard form of year
yearStr2 = tostring(year)
else -- some languages use different characters for numbers
yearStr2 = yearStr1
zeroStr = mw.ustring.sub(yearStr1,1,1) -- get "0" in whatever language
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
end
return string.gsub(datestr, yearStr1, yearStr2 ) -- in datestr replace long year with trimmed one
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * datecode: YMDHMS, YMDHM, YMD, YM, MD, Y, or M
-- * day : Number between 1 and 31 (not needed for most languages)
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function getDateFormat(datecode, day, lang)
local function parseFormat(dFormat, day)
if dFormat:find('default') and #dFormat>10 then
-- special (and messy) case of dFormat code depending on a day number
-- then json contains a string with more json containing "default" field and 2 digit day keys
-- if desired day is not in that json than use "default" case
dFormat = dFormat:gsub('”','"') -- change fancy double quote to a straight one, used for json marking
local D = mw.text.jsonDecode( dFormat ) --com = mw.dumpObject(D)
day = string.format('d%02i',day) -- create day key
dFormat = D[day] or D.default
dFormat = dFormat:gsub("'", '"') -- change single quote to a double quote, used for {{#time}} marking
end
return dFormat
end
local T = {}
local tab = mw.ext.data.get('DateI18n.tab', lang)
for _, row in pairs(tab.data) do -- convert the output into a dictionary table
local id, _, msg = unpack(row)
T[id] = msg
end
local dFormat = T[datecode]
if dFormat=='default' and (datecode=='YMDHMS' or datecode=='YMDHM') then
-- for most languages adding hour:minute:second is done by adding ", HH:MM:SS to the
-- day precission date, those languages are skipped in DateI18n.tab and default to
-- English which stores word "default"
dFormat = parseFormat(T['YMD'], day).. ', H:i'
if datecode=='YMDHMS' then
dFormat = dFormat .. ':s'
end
else
dFormat = parseFormat(dFormat, day)
end
return dFormat
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * month : month number
-- * case : gramatic case abbriviation, like "ins", "loc"
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function MonthCase(month, case, lang)
local T = {{},{},{},{},{},{},{},{},{},{},{},{}}
local tab = mw.ext.data.get('I18n/MonthCases.tab', lang)
for _, row in pairs(tab.data) do
local mth, cs, msg = unpack(row)
T[mth][cs] = msg
end
return T[month][case]
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
local Date = require('Module:DateI18n')._Date
local dateStr = Date({2020, 12, 30, 12, 20, 11}, lang)
Parameters:
* {year,month,day,hour,minute,second, tzhour, tzmin}: broken down date-time component strings or numbers
tzhour, tzmin are timezone offsets from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
if not lang or not mw.language.isValidCode( lang ) then
lang = mw.getCurrentFrame():callParserFunction( "int", "lang" ) -- get user's chosen language
end
if lang == 'be-tarsk' then
lang = 'be-x-old'
end
-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array
local datecode, datenum = parserDatevec(datevec)
local year, month, day = datenum[1], datenum[2], datenum[3]
local timeStamp, datecode = getTimestamp(datecode, datenum)
if not timeStamp then -- something went wrong in parserDatevec
return ''
end
-- Commons [[Data:DateI18n.tab]] page stores prefered formats for diferent
-- languages and datecodes (specifying year-month-day or just year of month-day, etc)
-- Look up country specific format input to {{#time}} function
local dFormat = getDateFormat(datecode, day, lang)
-- By default the gramatical case is not specified (case=='') allowing the format to be specified
-- in [[Data:DateI18n.tab]]. You can overwrite the default grammatical case of the month by
-- specifying "case" variable. This is needed mostly by Slavic languages to create more complex
-- phrases as it is done in [[c:Module:Complex date]]
case = case or ''
if (lang=='qu' or lang=='qug') and (case=='nom') then
-- Special case related to Quechua and Kichwa languages. The form in the I18n is
-- Genitive case with suffix "pi" added to month names provided by {#time}}
-- in Nominative case that "pi" should be removed
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
dFormat = dFormat:gsub('F"pi"', 'F')
elseif (case=='gen') then
dFormat = strReplace(dFormat, "F", "xg")
elseif (case=='nom') then
dFormat = strReplace(dFormat, "xg", "F")
elseif (case ~= '') then
-- see is page [[Data:I18n/MonthCases.tab]] on Commons have name of the month
-- in specific gramatic case in desired language. If we have it than replace
-- "F" and xg" in dFormat
local monthMsg = MonthCase(month, case, lang)
if monthMsg and monthMsg ~= '' then -- make sure it exists
dFormat = strReplace(dFormat, 'F', '"'..monthMsg..'"') -- replace default month with month name we already looked up
dFormat = strReplace(dFormat, 'xg', '"'..monthMsg..'"')
end
end
-- Translate the date using specified format
-- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate and
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions##time for explanation of the format
local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp) -- same as using {{#time}} parser function
-- Special case related to Thai solar calendar: prior to 1940 new-year was at different
-- time of year, so just year (datecode=='Y') is ambiguous and is replaced by "YYYY or YYYY" phrase
if lang=='th' and datecode=='Y' and year<=1940 then
datestr = string.format('%04i หรือ %04i', year+542, year+543 )
end
-- If year<1000 than either keep the date padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if year and year<1000 then
if type(trim_year)=='nil' then
trim_year = '100-999'
end
local trim = yesno(trim_year,nil) -- convert to boolean
if trim==nil and type(trim_year)=='string' then
-- if "trim_year" not a simple True/False than it is range of dates
-- for example '100-999' means to pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and year>=tonumber(YMin) and year<=tonumber(YMax))
end
if trim==true then
datestr = trimYear(datestr, year, lang) -- in datestr replace long year with trimmed one
end
end
-- append timezone if present
if datenum[7] and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
-- use {{#time}} parser function to create timezone string, so that we use correct character set
local sign = (datenum[7]<0) and '−' or '+'
timeStamp = string.format("2000-01-01 %02i:%02i:00", math.abs(datenum[7]), datenum[8] or 0)
local timezone = mw.language.new(lang):formatDate( 'H:i', timeStamp) -- same as using {{#time}} parser function
datestr = string.format("%s %s%s", datestr, sign, timezone )
end
-- html formating and tagging of date string
if class and class ~= '' and datecode~='M' and datecode~='MD'then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
{{#invoke:DateI18n|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
* year, month, day, hour, minute, second: broken down date-time component strings
* tzhour, tzmin: timezone offset from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p.Date(frame)
local args = {}
for name, value in pairs( frame.args ) do
name = string.gsub( string.lower(name), ' ', '_')
args[name] = value
end
return p._Date(
{ args.year, args.month, args.day, args.hour, args.minute, args.second, args.tzhour, args.tzmin },
args.lang, -- language
args.case, -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
return p
lcwmkh5p6iicle9dw7qzn6d8j6xkduv
803624
803623
2026-07-30T00:19:47Z
SM7
3953
11 revisions imported from [[:en:Module:DateI18n]]
803622
Scribunto
text/plain
--[[
__ __ _ _ ____ _ ___ _ ___
| \/ | ___ __| |_ _| | ___ _| _ \ __ _| |_ ___|_ _/ |( _ ) _ __
| |\/| |/ _ \ / _` | | | | |/ _ (_) | | |/ _` | __/ _ \| || |/ _ \| '_ \
| | | | (_) | (_| | |_| | | __/_| |_| | (_| | || __/| || | (_) | | | |
|_| |_|\___/ \__,_|\__,_|_|\___(_)____/ \__,_|\__\___|___|_|\___/|_| |_|
This module is intended for processing of date strings.
Please do not modify this code without applying the changes first at Module:Date/sandbox and testing
at Module:Date/sandbox/testcases and Module talk:Date/sandbox/testcases.
Authors and maintainers:
* User:Parent5446 - original version of the function mimicking template:ISOdate
* User:Jarekt - original version of the functions mimicking template:Date
]]
require('strict')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
-- Function allowing for consistent treatment of boolean-like wikitext input.
-- It works similarly to Module:Yesno
local function yesno(val, default)
if type(val) == 'boolean' then
return val
elseif type(val) == 'number' then
if val==1 then
return true
elseif val==0 then
return false
end
elseif type(val) == 'string' then
val = mw.ustring.lower(val) -- put in lower case
if val == 'no' or val == 'n' or val == 'false' or tonumber(val) == 0 then
return false
elseif val == 'yes' or val == 'y' or val == 'true' or tonumber(val) == 1 then
return true
end
end
return default
end
---------------------------------------------------------------------------------------
-- String replacement that ignores part of the string in "..."
local function strReplace(String, old, new)
if String:find('"') then
local T={}
for i, str in ipairs(mw.text.split( String, '"', true )) do
if i%2==1 then
str = str:gsub(old, new)
end
table.insert(T, str)
end
return table.concat(T,'"')
else
return String:gsub(old, new)
end
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datevec - Array of {year,month,day,hour,minute,second, tzhour, tzmin} containing broken
-- down date-time component strings or numbers
-- OUTPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - same array but holding only numbers or nuls
local function parserDatevec(datevec)
-- if month is not a number than check if it is a month name in project's language
local month = datevec[2]
if month and month~='' and not tonumber(month) then
datevec[2] = mw.getContentLanguage():formatDate( "n", month)
end
-- create datecode based on which variables are provided and check for out-of-bound values
local maxval = {nil, 12, 31, 23, 59, 59, 23, 59} -- max values for year, month, ...
local minval = {nil, 1, 1, 0, 0, 0, -23, 0} -- min values for year, month, ...
local c = {'Y', 'M', 'D', 'H', 'M', 'S', '', ''}
local datecode = '' -- a string signifying which combination of variables was provided
local datenum = {} -- date-time encoded as a vector = [year, month, ... , second]
for i = 1,8 do
datenum[i] = tonumber(datevec[i])
if datenum[i] and (i==1 or (datenum[i]>=minval[i] and datenum[i]<=maxval[i])) then
datecode = datecode .. c[i]
end
end
return datecode, datenum
end
---------------------------------------------------------------------------------------
-- process datevec
-- INPUT:
-- * datecode - a code specifying content of the array where Y' is year, 'M' is month,
-- 'D' is day, 'H' is hour, 'M' minute, 'S' is second. output has to be one of YMDHMS, YMDHM, YMD, YM, MD, Y
-- * datenum - Array of {year,month,day,hour,minute,second, tzhour, tzmin} as numbers or nuls
-- OUTPUT:
-- * timeStamp - date string in the format taken by mw.language:formatDate lua function and {{#time}} perser function
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
-- * datecode - with possible corrections
local function getTimestamp(datecode, datenum)
-- create time stamp string (for example 2000-02-20 02:20:20) based on which variables were provided
local timeStamp
if datecode == 'YMDHMS' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5], datenum[6] )
elseif datecode == 'YMDHM' then
timeStamp = string.format('%04i-%02i-%02i %02i:%02i', datenum[1], datenum[2], datenum[3], datenum[4], datenum[5] )
elseif datecode:sub(1,3)=='YMD' then
timeStamp = string.format('%04i-%02i-%02i', datenum[1], datenum[2], datenum[3] )
datecode = 'YMD' -- 'YMD', 'YMDHMS' and 'YMDHM' are the only supported format starting with 'YMD'. All others will be converted to 'YMD'
elseif datecode == 'YM' then
timeStamp = string.format('%04i-%02i', datenum[1], datenum[2] )
elseif datecode:sub(1,1)=='Y' then
timeStamp = string.format('%04i', datenum[1] )
datecode = 'Y'
elseif datecode == 'M' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], 1 )
elseif datecode == 'MD' then
timeStamp = string.format('%04i-%02i-%02i', 2000, datenum[2], datenum[3] )
else
timeStamp = nil -- format not supported
end
return timeStamp, datecode
end
---------------------------------------------------------------------------------------
-- trim leading zeros in years prior to year 1000
-- INPUT:
-- * datestr - translated date string
-- * lang - language of translation
-- OUTPUT:
-- * datestr - updated date string
local function trimYear(datestr, year, lang)
local yearStr0, yearStr1, yearStr2, zeroStr
yearStr0 = string.format('%04i', year ) -- 4 digit year in standard form "0123"
yearStr1 = mw.language.new(lang):formatDate( 'Y', yearStr0) -- same as calling {{#time}} parser function
--yearStr1 = mw.getCurrentFrame():callParserFunction( "#time", { 'Y', yearStr0, lang } ) -- translate to a language
if yearStr0==yearStr1 then -- most of languages use standard form of year
yearStr2 = tostring(year)
else -- some languages use different characters for numbers
yearStr2 = yearStr1
zeroStr = mw.ustring.sub(yearStr1,1,1) -- get "0" in whatever language
for i=1,3 do -- trim leading zeros
if mw.ustring.sub(yearStr2,1,1)==zeroStr then
yearStr2 = mw.ustring.sub(yearStr2, 2, 5-i)
else
break
end
end
end
return string.gsub(datestr, yearStr1, yearStr2 ) -- in datestr replace long year with trimmed one
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * datecode: YMDHMS, YMDHM, YMD, YM, MD, Y, or M
-- * day : Number between 1 and 31 (not needed for most languages)
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function getDateFormat(datecode, day, lang)
local function parseFormat(dFormat, day)
if dFormat:find('default') and #dFormat>10 then
-- special (and messy) case of dFormat code depending on a day number
-- then json contains a string with more json containing "default" field and 2 digit day keys
-- if desired day is not in that json than use "default" case
dFormat = dFormat:gsub('”','"') -- change fancy double quote to a straight one, used for json marking
local D = mw.text.jsonDecode( dFormat ) --com = mw.dumpObject(D)
day = string.format('d%02i',day) -- create day key
dFormat = D[day] or D.default
dFormat = dFormat:gsub("'", '"') -- change single quote to a double quote, used for {{#time}} marking
end
return dFormat
end
local T = {}
local tab = mw.ext.data.get('DateI18n.tab', lang)
for _, row in pairs(tab.data) do -- convert the output into a dictionary table
local id, _, msg = unpack(row)
T[id] = msg
end
local dFormat = T[datecode]
if dFormat=='default' and (datecode=='YMDHMS' or datecode=='YMDHM') then
-- for most languages adding hour:minute:second is done by adding ", HH:MM:SS to the
-- day precission date, those languages are skipped in DateI18n.tab and default to
-- English which stores word "default"
dFormat = parseFormat(T['YMD'], day).. ', H:i'
if datecode=='YMDHMS' then
dFormat = dFormat .. ':s'
end
else
dFormat = parseFormat(dFormat, day)
end
return dFormat
end
---------------------------------------------------------------------------------------
-- Look up proper format string to be passed to {{#time}} parser function
-- INPUTS:
-- * month : month number
-- * case : gramatic case abbriviation, like "ins", "loc"
-- * lang : language
-- OUTPUT:
-- * dFormat : input to {{#time}} function
local function MonthCase(month, case, lang)
local T = {{},{},{},{},{},{},{},{},{},{},{},{}}
local tab = mw.ext.data.get('I18n/MonthCases.tab', lang)
for _, row in pairs(tab.data) do
local mth, cs, msg = unpack(row)
T[mth][cs] = msg
end
return T[month][case]
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
local Date = require('Module:DateI18n')._Date
local dateStr = Date({2020, 12, 30, 12, 20, 11}, lang)
Parameters:
* {year,month,day,hour,minute,second, tzhour, tzmin}: broken down date-time component strings or numbers
tzhour, tzmin are timezone offsets from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p._Date(datevec, lang, case, class, trim_year)
-- make sure inputs are in the right format
if not lang or not mw.language.isValidCode( lang ) then
lang = mw.getCurrentFrame():callParserFunction( "int", "lang" ) -- get user's chosen language
end
if lang == 'be-tarsk' then
lang = 'be-x-old'
end
-- process datevec and extract timeStamp and datecode strings as well as numeric datenum array
local datecode, datenum = parserDatevec(datevec)
local year, month, day = datenum[1], datenum[2], datenum[3]
local timeStamp, datecode = getTimestamp(datecode, datenum)
if not timeStamp then -- something went wrong in parserDatevec
return ''
end
-- Commons [[Data:DateI18n.tab]] page stores prefered formats for diferent
-- languages and datecodes (specifying year-month-day or just year of month-day, etc)
-- Look up country specific format input to {{#time}} function
local dFormat = getDateFormat(datecode, day, lang)
-- By default the gramatical case is not specified (case=='') allowing the format to be specified
-- in [[Data:DateI18n.tab]]. You can overwrite the default grammatical case of the month by
-- specifying "case" variable. This is needed mostly by Slavic languages to create more complex
-- phrases as it is done in [[c:Module:Complex date]]
case = case or ''
if (lang=='qu' or lang=='qug') and (case=='nom') then
-- Special case related to Quechua and Kichwa languages. The form in the I18n is
-- Genitive case with suffix "pi" added to month names provided by {#time}}
-- in Nominative case that "pi" should be removed
-- see https://commons.wikimedia.org/wiki/Template_talk:Date#Quechua from 2014
dFormat = dFormat:gsub('F"pi"', 'F')
elseif (case=='gen') then
dFormat = strReplace(dFormat, "F", "xg")
elseif (case=='nom') then
dFormat = strReplace(dFormat, "xg", "F")
elseif (case ~= '') then
-- see is page [[Data:I18n/MonthCases.tab]] on Commons have name of the month
-- in specific gramatic case in desired language. If we have it than replace
-- "F" and xg" in dFormat
local monthMsg = MonthCase(month, case, lang)
if monthMsg and monthMsg ~= '' then -- make sure it exists
dFormat = strReplace(dFormat, 'F', '"'..monthMsg..'"') -- replace default month with month name we already looked up
dFormat = strReplace(dFormat, 'xg', '"'..monthMsg..'"')
end
end
-- Translate the date using specified format
-- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#mw.language:formatDate and
-- https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions##time for explanation of the format
local datestr = mw.language.new(lang):formatDate( dFormat, timeStamp) -- same as using {{#time}} parser function
-- Special case related to Thai solar calendar: prior to 1940 new-year was at different
-- time of year, so just year (datecode=='Y') is ambiguous and is replaced by "YYYY or YYYY" phrase
if lang=='th' and datecode=='Y' and year<=1940 then
datestr = string.format('%04i หรือ %04i', year+542, year+543 )
end
-- If year<1000 than either keep the date padded to the length of 4 digits or trim it
-- decide if the year will stay padded with zeros (for years in 0-999 range)
if year and year<1000 then
if type(trim_year)=='nil' then
trim_year = '100-999'
end
local trim = yesno(trim_year,nil) -- convert to boolean
if trim==nil and type(trim_year)=='string' then
-- if "trim_year" not a simple True/False than it is range of dates
-- for example '100-999' means to pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
local YMin, YMax = trim_year:match( '(%d+)-(%d+)' )
trim = (YMin~=nil and year>=tonumber(YMin) and year<=tonumber(YMax))
end
if trim==true then
datestr = trimYear(datestr, year, lang) -- in datestr replace long year with trimmed one
end
end
-- append timezone if present
if datenum[7] and (datecode == 'YMDHMS' or datecode == 'YMDHM') then
-- use {{#time}} parser function to create timezone string, so that we use correct character set
local sign = (datenum[7]<0) and '−' or '+'
timeStamp = string.format("2000-01-01 %02i:%02i:00", math.abs(datenum[7]), datenum[8] or 0)
local timezone = mw.language.new(lang):formatDate( 'H:i', timeStamp) -- same as using {{#time}} parser function
datestr = string.format("%s %s%s", datestr, sign, timezone )
end
-- html formating and tagging of date string
if class and class ~= '' and datecode~='M' and datecode~='MD'then
local DateHtmlTags = '<span style="white-space:nowrap"><time class="%s" datetime="%s">%s</time></span>'
datestr = DateHtmlTags:format(class, timeStamp, datestr)
end
return datestr
end
--[[ ========================================================================================
Date
This function is the core part of the ISOdate template.
Usage:
{{#invoke:DateI18n|Date|year=|month=|day=|hour=|minute=|second=|tzhour=|tzmin=|lang=en}}
Parameters:
* year, month, day, hour, minute, second: broken down date-time component strings
* tzhour, tzmin: timezone offset from UTC, hours and minutes
* lang: The language to display it in
* case: Language format (genitive, etc.) for some languages
* class: CSS class for the <time> node, use "" for no metadata at all
]]
function p.Date(frame)
local args = {}
for name, value in pairs( frame.args ) do
name = string.gsub( string.lower(name), ' ', '_')
args[name] = value
end
return p._Date(
{ args.year, args.month, args.day, args.hour, args.minute, args.second, args.tzhour, args.tzmin },
args.lang, -- language
args.case, -- allows to specify grammatical case for the month for languages that use them
args.class or 'dtstart', -- allows to set the html class of the time node where the date is included. This is useful for microformats.
args.trim_year or '100-999' -- by default pad one and 2 digit years to be 4 digit long, while keeping 3 digit years as is
)
end
return p
lcwmkh5p6iicle9dw7qzn6d8j6xkduv
तिलंगी
0
69954
803323
762645
2026-07-29T16:20:53Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803323
wikitext
text/x-wiki
[[File:Kitesflying.jpg|right|thumb|alt=आसमान में उड़त कई किसिम के पतंगा|किसिम किसिम के पतंगा]]
'''तिलंगी''' भा '''पतंगा''' एगो अइसन जीनिस चाहे वस्तु हऽ जेकरा सुता के मदद से उड़ावल जाला। तिलंगी एगो पाँख नियन सतह के बनल होखेला जेकरा मे एगो सुता बन्हल रहेला। कोनो तिलंगी तबे उड़ेले जब हावा के दाबाव सतह के उपरा कम आ नीचवा बेसी होला।<ref>{{cite web |url=http://www.grc.nasa.gov/WWW/K-12/airplane/guided.htm |title=Beginner's Guide to Aeronautics |publisher=NASA |accessdate=2012-10-03 |archive-date=2015-03-25 |archive-url=https://web.archive.org/web/20150325054529/http://www.grc.nasa.gov/WWW/K-12/airplane/guided.htm |url-status=dead }}</ref> हावा जवना देने बहेला तवना देने तिलांगीयो जाले। एहिसे तिलंगी पऽ दुगो [[बर]] लागेला, एगो ऊपर देने आ एगो सोझा, एह दुनो बर के चलते जवन कुलि परिनाम बर तिलंगी पऽ लागेला तवना के सुता मे उत्त्पन भइल [[तान (भओतिकी)|तान]] सम्हार लेवेला।
द्रव्यन के सिधांत सभ पनियो प लागेला एहिसे तिलंगी के परयोग पनियो मे कइल जा सकेला बाकिर आजकाल ई परचलन मे नइखे।<ref>{{cite web|url=http://2lo.de/kitedive/index.htm |title=Underwater kiting |publisher=2lo.de |date= |accessdate=2012-10-03}}</ref><ref>{{cite web|url=http://www.google.com/patents?id=xldbAAAAEBAJ |title=Hydro kite angling device Jason C. Hubbart |date= |accessdate=2012-10-03}}</ref>
==संदर्भ==
{{reflist}}
[[श्रेणी:खेलौना]]
{{Game-stub}}
2ekytvv8zv5x69em0dk85tqgldh3yr1
समय अवकलन
0
69992
803356
658237
2026-07-29T16:23:13Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803356
wikitext
text/x-wiki
'''समय अवकलन''' [[समय]] के सपेक्ष मे केवनो [[फलन]] के [[अवकलन]] होखेला।
==चिन्ह==
समय अवकलन के लउकावे ला कय गो चिन्ह के परयोग कइल जाला जेहमे सभे से सरल लेबनीज के चिन्ह हऽ:
:<math>\frac {dx} {dt}</math>
भौतिकी मे एगो आउर चिन्ह के परयोग होखेला जेवना मे [[फलन]] के ऊपर एगो बिंदू लगाई के होकर समय अवकलन के लऊकावल जाला आ हेकरा न्यूटन के चिन्ह कहल जाला:
:<math>\dot{x}</math>
उंच समय अवकलनो के परयोग होखेला। समय के सापेक्ष मे [[दोसरका अवकलन]] के अइसे लिखल जाला:
:<math>\frac {d^2x} {dt^2}</math>
जेकर छोटहन रूप <math>\ddot{x}</math> हऽ।
कोनो सादिक जइसे:
:<math> \vec V = \left[ v_1,\ v_2,\ v_3, \cdots \right] \ , </math>
के समय अवकलन के होकर घटक सभ के समय अवकलन से बने वला सदिक होखेला:
:<math> \frac {d \vec V } {dt} = \left[ \frac{ d v_1 }{dt},\frac {d v_2 }{dt},\frac {d v_3 }{dt}, \cdots \right] \ . </math>
== भओतिकी मे परयोग==
समय अवकलन [[भौतिकी]] के एगो चिन्हल सिधांत हटे, कोनो बदलत [[पोजिसन (वेक्टर)|स्तिथि सदीक]] <math>x\,</math> ला होकर समय अवकलन <math>\dot{x}</math> [[वेग]] आ दोसरका समय अवकलन <math>\ddot{x}</math> परवेग होला। कबो कबो आऊरो ऊंच अवकलन के परयोग होखेला: समय के सापेक्ष मे स्तिथि के तीसरका अवकलन के [[जरक]] कहल जाला। [[गति के लेखाचित्र आउर अवकलन]] देखीं।
भओतिकी मे बहुते बेसी समीकरन सभ मे समय के सापेक्ष पहिलका चाहे दोसरका अवकलन होला।भौतिकी के ढेरे मत्रक एक दोसरा के समय अवकलन होखेलऽसन:
* [[बर]] [[वेगमान]] के समय अवकलन बा।
* [[तागत (भओतिकी)|तागत]] [[ऊर्जा]] के समय अवकलन बा।
* [[बिद्युत धार]] [[बिद्यूत भार]] के समय अवकलन बा।
आ अइसन अइसन ढेरे मात्रक हवऽसन।
भओतिकी मे सदिक सभ के समय अवकलन कय हाली सोझा आवेला जइसे [[वेग]]। अइसन अवकलन मे सदिक के मान अउरी दिसा दून्नो समय प नीरभर होखेला।
===उदाहरन : बृतिय गती===
[[Image:polar rectangular.svg|thumb|300px|कारटीसियन निर्देशांक (''x'',''y'') आऊर ध्रुवीय निर्देशांक (''r'',''θ'') मे संबंध।]]
उदाहरन बदे, मान लिहीं की एगो कन गोल बांट प चलऽता। होकर स्तिथि <math>r=x\hat{\imath}+y\hat{\jmath}</math> सदिक से लउकावल जाता, तऽ ओह कन के ध्रुवीय स्तिथि निर्देशांक हई होई:
:<math>\begin{align}
x &= r \cos(\theta) \\
y &= r \sin(\theta)
\end{align}</math>
उदाहरन बदे, मान लिहीं की {{Nowrap|1=''θ'' = ''t''}}, तऽ कोनो छन स्तिथि होई:
:<math>\mathbf{r}(t) = r\cos(t)\hat{\imath}+r\sin(t)\hat{\jmath}</math>
ई रूप देखावऽता की '''''r'''''(''t'') से लउकावल गति एगो '''r''' त्रीज्या के बृत प होखऽता।
गती के इहो रूप ले [[वेग]] निकारल जा सकेला, जेवन की एह रूप के समय अवकलन होई। कोनो सदिक के समय अवकलन होकर घटक सभ के समय अवकलन ले बनल सदिक होखेला। एहिसे एहिजा वेग सदिक हई होई:
:<math>
\begin{align}
\mathbf{v}(t) = \frac {d\,\mathbf{r}(t) }{dt} &= r \left[\frac{d\, \cos(t)}{dt}, \frac{d\, \sin(t)}{dt} \right] \\
&= r\ [ -\sin(t),\ \cos(t)] \\
&= [-y (t), x(t)].
\end{align}</math>
एहिसे बृतिय गति मे भले स्तिथि ना बदले बाकिर वेग सुन्ना ना होखेला। वेग अउरी स्तिथी सादिक महे ९०° के कोन बनेला, जेकरा [[अदिक गुना]] ले सिध कइल जाई सकेला।
:<math>\mathbf{v} \cdot \mathbf{r} = [-y, x] \cdot [x, y] = -yx + xy = 0\, . </math>
तब [[परवेग]] वेग के समय अवकलन होई:
:<math>\mathbf{a}(t) = \frac {d\, \mathbf{v}(t)}{dt} = [-x(t), -y(t)] = -\mathbf{r}(t)\, .</math>
एह परवेग सदिक के दिसा अक्ष देने होला जेकरा [[केंद्रक परवेग]] कहल जाला।
{{Math-stub}}
sng3b3fnd1kdw1sr2347muwl77xbvvy
अवकलन
0
70032
803300
658246
2026-07-29T16:06:52Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803300
wikitext
text/x-wiki
'''अवकलन''' कोनो [[असल चरक वला फलन]] के मान मे होकर [[चरक]] मे बदलाव के साथे आवे वला बदलाव के भागा होखेला। अवकलन [[कलन]] के एगो जरूरी अउजार हटे। उदाहरन बदे, स्थिति सदिक के [[समय]] के सपेक्ष् अवकलन [[वेग]] होला: हेकरा ले ई नपाला की कोनो चीझ कतना हाली हाली आपन अस्थान मे परीबरतन करऽता।
खाली एगो चरक वला फलन के अवकलन कोनो निश्चित बिंदु प [[स्पर्श पाई]] के प्रवनता चाहे स्लोप देवेला। अवकलन सभ के [[एक से बेसी असल चरक वला फलन]] सभो जोरे परयोग कइल जा सकेला।
[[श्रेणी:गणित]]
{{Math-stub}}
0d2tt9zctrtmlv8yazd30qjmtkcq3ss
गति
0
72111
803321
752785
2026-07-29T16:20:45Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803321
wikitext
text/x-wiki
[[भौतिकी]] मे '''गति''', समय जोर कोनो बस्तु के स्थान मे परीवरतन के कहल जाला। गनित मे गति के [[दुरत्व]], [[वेग]], [[परवेग]] के भेस मे लउकावल जाला। कोनो बस्तू के गति के निरिक्षण होकरा जोरे एगो [[परसंग परनाली]] लगाइके कइल जाला।
जदि कोनो परसंग परनाली ले देखला प बस्तू के स्थान मे परिवरतन नइखे होखत हेकर माने हई हऽ की ऊ बस्तु '''असथिर''' बा। बिना परसंग परनाली के गति के बिबरन ना दिहल जा सकेला। एहिसे ब्राम्हण के सभे बस्तु के गतिमान मानल जा सकेला।<ref name=Tyson>{{cite book|last=Tyson|first=Neil de Grasse|author2=Charles Tsun-Chu Liu|author3=Robert Irion|title=The universe : at home in the cosmos|year=2000|publisher=[[National Academy Press]]|location=Washington, DC|isbn=978-0-309-06488-0|url-access=registration|url=https://archive.org/details/oneuniverse00neil}}</ref>
कोनो चीझा के स्थान, चाहे अस्तिथी मे बदलाव के गति कहल जा सकेला। उदाहरन बदे, केहू [[तरंग]] चाहे कवाँटम कन के गति बदे बतिया सकेला। गति के नापे वला मुख्य मात्रा [[वेगमान]] हटे। बस्तु के वेगमान होकर [[भार]] आउर [[वेग]] संगे अधिकावेला। अइसन बेबसथा जेहमे बहरा से कोनो [[बर]] ना लागेला, होकर वेगमान समय संगे ना बदलेला, एहिसे कोनो बस्तु के गति बिना ओहपऽ बर लगवले नइखे बदलल जा सकत।
== गति के नियम ==
{{Main|बरबिद्या}}
भौतिकी मे, भारी बस्तुअन के गति के बरनन दुगो गति के [[बैज्ञानिक नियम|नियम]] से होखेला। [[ब्रह्मांड]] के सभ बड़हन बस्तुअन के गति के बरनन [[क्लासिकल मैकेनिक्स]] के परयोग के होखेला आ छोटहन कन सभे के [[क्वांटम मैकेनिक्स]] के परयोग के होखेला। गति के तीनिगो नियम [[आइजक न्युटन|न्यूटन]] दिहले बाड़न।
{| class="wikitable"
|'''पहिलका नियम''':
|कोनो [[जड़ परसंग परनालि]] मे, कोनो बस्तु तबले '''अस्थिर''' रहेला चाहे एक्के [[वेग]] से चलेला, जबले ओहपऽ बहरा से केवनो [[बर]] ना लगावल जाला।
|-
|'''दूसरका नीयम''':
|कोनो [[जड़ परसंग परनालि]] मे, बस्तु प लगावल सउँसे [[बर]] सदिकन के जोड़ ओह बस्तु के [[भार]] m आउर [[परवेग]] '''a''' के गूना होखेला।: '''F''' = ''m'''''a'''.
|-
|'''तीसरका नियम''':
|जवन खानी एगो बस्तु दोसरा बस्तु प बर लगवेला, ओही खानी दोसरका बस्तु पहिलका प बराबर बर बिपरित दिसा ने लगवेला।
|}
=== चिरायत बरबिद्या ===
चिरायत बरबिद्या के परयोग बड़हन कनन के गति बतावे ला कइल जाला। आपन दायरा मे की एकदम सटिक परिनाम देवेला। चिरायत बरबिद्या [[न्यूटन के गति के नियम]] प निरभर रहेला। ई नियम सभ कोनो चीझ के गति आ ओहपे लागत बर के संबंध देखावेलऽ सन। एखिनी के पहिला हाली [[आइजक न्युटन]] जी आपन किताब [[प्रिंसिपिया नेचुरालिस प्रिंसिपिया मेथेमेटीका]] मे लिखले रहनी जेवन 5 जुलाई 1684 के छपल रहे।
=== सपेक्ष बरबिद्या ===
आधुनिक गतीबिज्ञान जेवन की [[विद्युत्चुम्बकत्व]] दिया सोध कई के बिकसित भइल बा, ओहमें कुलि वेगन के [[अँजोरिया के गतिवेग]] संगे भागा लगाई के देखावल जाला। हेकरे के [[बिशेस सापेक्षता]] कहल जाला।
=== क्वांटम मैकेनिक्स ===
क्वांटम मैकेनिक्स कर छोटहन कन सभ के गति के बुझे जाने बदे परयोग होला। हेकर परयोग [[अती तरलता]] खानी जरूरी आ अबुझ चीजन के बूझे बदे कइल जाला।
== मनुस के ना बूझावे वला गातियन के सूची ==
ब्रह्माण्ड के आउर सभ बस्तुअन खानी हरदम गति मे रहेला बाकिर एह सभ के अलावे कुछु अइसनो अइसनो गति होला जेकरा देखल ना जा सके चाहे देखल कठिन होखेला।
=== ब्रह्माण्ड ===
[[काल-अवकाश]] के [[ब्रह्माण्ड के बिस्तार|छितरे]] के कारन ई हऽ की [[ब्रह्मांड]] के हर चीझ एगो रबर बैंड खानी छितरऽता। ई गति अस्पष्ट बा आ भौतिक नइखे।
=== गैलेक्सी ===
बहुते बैज्ञानिक ई मानेलन कि [[आकाशगंगा]] ब्रह्माण्ड में घुरऽता जेकर गति 600 km प्रति सेकेंड हवे।
=== सूरूज आ गरहा ===
सूरूज आकाशगंगा जोरे घुरऽता आ सूरूज के चहुदिसी मय गरहा सभ घुरेलऽसन।
=== धरती ===
धरती आपन अक्ष प घुरेले, [[दिन]] आ [[रात]] हेकरा परमानित करेला। धरती सूरजो के चारो देने घूरेले आ एक बरिस मे आपन एक घुमरि पुरावेले।
=== महादेस ===
[[प्लेट टेक्टॉनिक्स]] के सिधांत कहेला की [[महादीप]] सभ धरती के सतह प गति मे बाड़ऽसन।
=== भितारिया देह ===
मनुस के [[दिल|जियरा]] निरंतर काज करत रहेला।
== अँजोर ==
{{main|अँजोरिया के गतिवेग}}
अंजोरा 299,792,458 m/s के गति से चलेला, निर्यात मे एहसे तेज कूछुवो ना चले। हेकर संगे अँजोरिया के गति परसंग परनाली प निरभर ना करे।
== गति के परकार ==
* [[सरल आवर्त गति]]: पेंडुलम के गति।
== संदर्भ ==
{{Reflist}}
[[श्रेणी:भौतिकी]]
{{Physics-stub}}
3wvmibize3sf7hbci2nhi89wwxpeomm
बबल छँटाई
0
72199
803322
776983
2026-07-29T16:20:49Z
SM7Bot
7808
/* बिधी जोरे उदाहरण */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803322
wikitext
text/x-wiki
'''बबल छँटाई''' एगो [[छँटाई के बिधी]] हऽ जेवन की सऊँसे लिस्ट मे बेर बेर घूरेला, अगिला चिक्ष के पिछलका से तूलना करेला आ जदि ऊ दुन्नो सही क्रम मे ना रहे तऽ दून्नो के [[अदला-बदली (प्रोगरमींग)|अदला-बदली]] कर देवेला। एहमें तबले लिस्ट भ मे घुरल जाला जबले पूरा लिस्ट के छँटाई ना हो जाला। भले ई बिधि बहुते सरल बा बाकिर बहुते लहे लहे काम करेला आ कतना समस्या सभ ला अप्ययोगिक बा।
{{Infobox Algorithm
|class=[[Sorting algorithm]]
|image=Bubblesort-edited-color.svg|
| caption=Static visualization of bubble sort<ref>{{cite web |last=Cortesi |first=Aldo |title=Visualising Sorting Algorithms |url=https://corte.si/posts/code/visualisingsorting/index.html |date=27 April 2007 |accessdate=16 March 2017}}</ref>
|data=[[Array data structure|Array]]
|time=<math>O(n^2)</math> comparisons, <math>O(n^2)</math> swaps
|average-time=<math>O(n^2)</math> comparisons, <math>O(n^2)</math> swaps
|best-time=<math>O(n)</math> comparisons, <math>O(1)</math> swaps
|space=<math>O(1)</math> auxiliary
|optimal=No
}}
==बिबेचन==
[[File:Bubble-sort-example-300px.gif|300px|thumb|right|बबल छँटाई बीधी के एगो उदाहरन, लिस्ट के पहिला अंग से चालू करीं, अगिला मय जोड़ा के मिलाईं, जदि दुन्नो सही क्रम मे नइखे तऽ दुन्नो के अदला बदली कर दिहीं। लिस्ट मे तबले घुरीं जबले सभ अंग एगो बिसेस क्रम मे नइखे आ जात।]]
===परदरसन===
बबल छँटाई के सभसे खराब औसत समय जटिलता [[बड़ O चिन्हा|O]](n ²) हऽ, जेने n छाँटे वला चीझन के संख्या हऽ। बसी समय लिहला के चलते ई बीधि अप्ययोगिक बा।
===बिधी जोरे उदाहरण===
एगो त्राम चाहे अएरे लिहीं "5 1 4 2 8" आ हेकरा बढ़त क्रम मे छाँटी। हर डेग प '''मोटहन''' अंग के मिलावल जाई। लिस्ट मे तीन हाली घुरे के जाओरत पड़ी।
'''पहिलका घुमरी'''
:( '''5''' '''1''' 4 2 8 ) → ( '''1''' '''5''' 4 2 8 ), एहिजा, पहिलका दुगो अंकन के मिलावल जाता आ अदला बदली कइल जाता। एहसे के 5>1
:( 1 '''5''' '''4''' 2 8 ) → ( 1 '''4''' '''5''' 2 8 ), बदल दिहिन एहसे की 5 > 4
:( 1 4 '''5''' '''2''' 8 ) → ( 1 4 '''2''' '''5''' 8 ), बदल दिहिं एहसे की 5 > 2
:( 1 4 2 '''5''' '''8''' ) → ( 1 4 2 '''5''' '''8''' ), अबहिन, ई दुन्नो पहिलहीं क्रम मे बाड़न सन(8 > 5), तऽ एखिनी के अदला बदली कइल जाई
;दूसरका घुमरी
:( '''1''' '''4''' 2 5 8 ) → ( '''1''' '''4''' 2 5 8 )
:( 1 '''4''' '''2''' 5 8 ) → ( 1 '''2''' '''4''' 5 8 ), बदल दिहिं एहसे की 4 > 2
:( 1 2 '''4''' '''5''' 8 ) → ( 1 2 '''4''' '''5''' 8 )
:( 1 2 4 '''5''' '''8''' ) → ( 1 2 4 '''5''' '''8''' )
अबहिन, लिस्ट क्रम मे आ गइल बा, बाकिर बिधी नइखे जानत के क्रम मे बा की ना. हेकरा बदे लिस्ट मे फेर एक हाली घूरे के पड़ी
;तीसरका घूमरी
:( '''1''' '''2''' 4 5 8 ) → ( '''1''' '''2''' 4 5 8 )
:( 1 '''2''' '''4''' 5 8 ) → ( 1 '''2''' '''4''' 5 8 )
:( 1 2 '''4''' '''5''' 8 ) → ( 1 2 '''4''' '''5''' 8 )
:( 1 2 4 '''5''' '''8''' ) → ( 1 2 4 '''5''' '''8''' )
== संदर्भ ==
{{Commons category}}
{{Reflist|33em}}
[[श्रेणी:कंप्यूटर बिज्ञान]]
{{Comp-sci-stub}}
htrmcnmvebiuhm8180zycwrl6zjr6g6
बैज्ञानिक नियम
0
72268
803287
781473
2026-07-29T16:05:52Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803287
wikitext
text/x-wiki
'''बैज्ञानिक नियम''' ({{Lang|en|scientific law}}) अइसन कहनाम चाहे कथन के कहल जाला जे परयोगन से सिध कइल रहले चाहे कोनो भौतिक घटना के बीबरन दिहेला।
[[श्रेणी:बिज्ञान|नियम]]
{{Science-stub}}
9fgdcmc5kynaldgv3zh949mly8ldb1y
मोस्फेट
0
72298
803298
734413
2026-07-29T16:06:44Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803298
wikitext
text/x-wiki
[[file:MOSFET Structure.png|thumb|upright=1.6|मोस्फेट, दुआर (G), देह (B), स्त्रोत (S) अउरी निकास (D) देखवात।]]
'''मोस्फेट''' एगो [[क्षेत्र परभावित ट्राजिस्टर]] हऽ, जेकरा भिरी एगो गेट होला जेकर [[बिभव]] तंत्र के चालकता बतावेला। बीभव संगे चालकता बदले के एह सकान के परयोग बिद्यूत संकेत के परवर्धन मे कइल जाला। मोस्फेट के अविष्कार इजीप्ट के अभियंता [[मोहम्मद एम आत्तल्लाह]] जी अउरी कोरिआ के अभियांता [[दावों कांग]] जी 1969 मे कइले रहनीं। ई [[रीनानु बिज्ञान]] के नेव हऽ आ इतिहास मे सभसे बेसि बने वला तंत्र बाटे।
csn1bkra37ppq0nhwzkzdezg4bzoun1
विद्युत्चुम्बकत्व
0
72302
803288
721497
2026-07-29T16:05:57Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803288
wikitext
text/x-wiki
'''विद्युत्चुम्बकत्व''' [[भौतिकी]] के एगो डाढ़ ह जेहमे [[बिद्यूतभार]] सभ के बीचे लागे वला [[बर]] प सोध कइल जाला।
[[श्रेणी:भौतिकी]]
{{Physics-stub}}
mz0e6qtm09dw7bc65kx9s26vhs1gqk4
वेक्टर स्पेस
0
72586
803290
721562
2026-07-29T16:06:05Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803290
wikitext
text/x-wiki
'''सदिक समुह''' मे [[सदिक|सदिकन]] के सञित के धइल जाला आ ओखिनी के जोड़, घटाऊ आ कोनो [[अदिक]] जोरे गुना कइल जाला। अदिक जादे तर कोनो [[सत संख्या]] के लिहल जाला।
[[श्रेणी:गणित]]
{{Math-stub}}
hu21s52ve73pn0yn1japus3r4b73ca3
सीरीज (गणित)
0
72616
803286
721485
2026-07-29T16:05:37Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803286
wikitext
text/x-wiki
[[गणित]] मे, '''लरी''' के जदि धिल रूप से परिभसित कइल जाए त ई एगो अइसन काज हऽ जेकरा मे अनंत मात्रा सभ के पारा पारी जोड़ल जाला।
[[श्रेणी:गणित]]
{{Math-stub}}
j6skbhrkqdo6hjwpopbwqqtv36131eb
बिदेसिया (तमासा)
0
75784
803303
774441
2026-07-29T16:07:04Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803303
wikitext
text/x-wiki
{{infobox play
| name = बिदेसिया
| writer = [[भिखारी ठाकुर]]
| chorus =
| characters =
| mute =
| setting =
| premiere = 1912
| place =
| orig_lang =
| series =
| subject = [[पलायन]], [[महिला सशक्तिकरण]], [[गरीबी]]
| genre = [[रंगमंच]]
| web =
}}
'''बिदेसिया''' (कैथी: 𑂥𑂱𑂠𑂵𑂮𑂱𑂨𑂰) चाहे '''बहरा बहार''' [[भिखारी ठाकुर]] के लिखल एगो [[भोजपुरी]] [[तमासा]] हऽ। ई भिखारी ठाकुर के गरीबी, पलायन आ नारी सशक्तिकरण प लिखल नाटक सभ मे से एगो बा।<ref>{{Cite journal|last=Singh|first=Dhananjay|date=September 2018|title=The Image of Women in Folk-Traditions of Migration|url=https://tiss.edu/uploads/files/4._Dhananjay_Singh.pdf|journal=Journal of Migration Affairs|volume=1|pages=55|via=the image of women in Bhojpuri folk- culture/literature of migration is colourful. The women themselves are in a way suffering from migration—whether they have been left behind, or if they have been forced to migrate themselves, or if they have come with her husband to his village as an urhari. The seriousness with which Bhikhari Thakur has dealt with the travails of these women is unparalleled. In his play ‘Bidesiya’ he shows Pyari Sundari’s suffering as the ideal chaste wife waiting for her husband in the village}}</ref> ई नाटक आपन ख्याती के चलते एगो भोजपुरी भाषा के एगो नाट्य शैली बनि गइल, आ दोसरो भाषा मे एह शैली मे तमासा लिखाइल, ओह घड़ी के बिद्वान लोग एकर प्रसिद्धि के तुलना रामायण से कइल बा।<ref>{{Cite book|last=यांग|first=आनंद |title=The Limited Raj: Agrarian Relations in Colonial India, Saran District, 1793-1920|publisher=Univ of California Press|year=2021|isbn=9780520369108}}</ref>
एह तमासा मे उनइसवा सताबदी के भोजपुरी समाज मे मेहरारुन के दुरदसा के देखावल गइल बा। एह नाटक के 1917 मे लिखल गइल रहे।<ref>{{Cite book|last=नारायन|first=बद्री|title=Culture and Emotional Economy of Migration|publisher=Taylor and Francis|page=74}}</ref>
==नांव==
एकरा पहिलका बेरा कलजुग प्रेम नांव से परकासित कइल गइल रहे, फेर बहरा बहार नांव से छपाईल, बाकिर एकर बिदेसिया नांव के पाट के चलते ई एही नांव से परसिद्ध भइल।<ref>{{Cite book|title=Bhikhari Thakur: Bhojpuri Ke Bharatendu|publisher=Aashu Prakashan|location=Allahabad}}</ref>
== संदर्भ ==
<br />
{{Play-stub}}
givroicyrq0i6fabl3r94xvlqooc4dd
राजमोहन
0
76172
803333
774904
2026-07-29T16:21:34Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803333
wikitext
text/x-wiki
{{Infobox musical artist
| name = राज मोहन
| native_name =
| image =
| caption = राज मोहन 2019 में
| image_size = 250
| background = सोलो गायक
| birth_name = राज मोहन<ref>{{cite web|url=https://www.thelallantop.com/videos/interview-of-raj-mohan-a-netherlands-based-bhojpuri-singer-musician-and-lyricist/|title=नीदरलैंड्स में रहे वाला सिंगर राज मोहन ले सुनी बिदेस में गावे वाला भोजपुरी गीत|publisher=thelallantop.com|}}</ref>
| birth_date = {{Birth date and age|1964|08|28|df=y}}
| birth_place = [[पारामारिबो]], [[सूरीनाम]]
| death_date =
| genre = पॉप , ग़ज़ल<ref>{{cite web|url=https://navbharattimes.indiatimes.com/metro/mumbai/other-news/raj-mohan-is-making-bhojpuri-songs-popular-in-suriname/amp_articleshow/67220654.cms|title=राज मोहन : सूरीनाम में भोजपुरी गाना के प्रसिद्ध करत बाड़न|publisher=navbharattimes.com|}}</ref>
| occupation = ब्यास, गीतकार
| years_active = 1988–अभिले
| associated_acts =
| label =
| website =
}}
'''राजमोहन''' एगो डच [[भोजपुरी]] गायक हवन। ई सरनामी भोजपुरी मे ढेरे गीत लिखले आ गवले बाड़न।<ref>{{cite web|url=https://wap.business-standard.com/article-amp/pti-stories/raj-mohan-the-man-popularising-bhojpuri-songs-in-suriname-118122300506_1.html|title=राज मोहन: भोजपुरी गीत के सूरीनाम में लोकप्रिय करे वाला गायक|publisher=business-standard.com|4=|access-date=2020-09-01|archive-date=2018-12-23|archive-url=https://web.archive.org/web/20181223190159/https://wap.business-standard.com/article-amp/pti-stories/raj-mohan-the-man-popularising-bhojpuri-songs-in-suriname-118122300506_1.html|url-status=dead}}</ref> 2011 में इ आपन पहिला पॉप एल्बम रिलीज़ कइलन जेकरा में सरनामी-भोजपुरी बोल के हिंदी गाना अउरी कविता संगे मिलावाल रहे, सब इंकर लिखल अउरी संगीत दिहल रहे।<ref>{{cite web|url=https://hindi.timesnownews.com/amp/bollywood/bollywood/article/bhojpuri-singer-raj-mohan-wants-to-make-bhojpuri-music-in-every-genre-says-in-exclusive-interview/397006|title=मुश्किलों में संगीत के ना साथ छोड़ले, इ गायक नीदरलैंड में रहके भोजपुरी में पहचान बनवलन|publisher=hindi.timesnownews.com|4=|access-date=2020-09-01|archive-date=2019-04-09|archive-url=https://web.archive.org/web/20190409072303/https://hindi.timesnownews.com/amp/bollywood/bollywood/article/bhojpuri-singer-raj-mohan-wants-to-make-bhojpuri-music-in-every-genre-says-in-exclusive-interview/397006|url-status=dead}}</ref> इ पहिलका बेरी रहे सूरीनाम के इतिहास में की सरनामी-भोजपुरी के कंटेम्पररी के रूप में इस्तेमाल कइल गइल।<ref>{{cite web|url=https://scroll.in/article/743625/the-sounds-of-surinam-when-bhojpuri-creole-meets-carribbean-beats|title=सूरीनाम के संगीत: जब भोजपुरी-क्रियोल मिलल कैरेबियन ताल संगे|publisher=scroll.in|}}</ref> राज मोहन आपन एल्बम 'कंट्राकी (2005)' ले सरनामी-भोजपुरी गीत के इजात कइलन, गीत अउरी ग़ज़ल स्टाइल में, जेकर कुल्ह दुनिया में बड़ाई भइल।<ref>{{cite web|url=http://www.rajmohan.nl/|title=राजमोहन के जीवनी|3=|access-date=2020-09-01|archive-date=2020-09-24|archive-url=https://web.archive.org/web/20200924173814/http://www.rajmohan.nl/|url-status=dead}}</ref>
== शुरूआती जिनगी अउरी पेशा ==
राज सूरीनाम केें पंचवा-पीढ़ी बिहारी-भारतीय बाड़न, इंकर पुरखन के अंग्रेजन सूरीनाम में गिरमिटिया मजदूर बना के ले गइल रहन सन। इ अट्रेक्ट, नीदरलैंड में बड भइलन अउरी छोट उमिर में संगीत में रूचि देखऽवलन।<ref>{{cite web|url=https://zeenews.india.com/hindi/entertainment/bhojpuri/raj-mohan-popular-songs-are-made-in-bhojpuri-songs-in-suriname/482037/amp|title=बिदेस के धरती प भोजपुरी गीतन के प्रसिद्धि दिलावत बाड़े म्यूजिशियन राज मोहन|publisher=Zee News|access date=}}</ref>
12 के उमिर में राज [[सूरीनाम]] ले [[नीदरलैंड]] चल गइले अपना बाबु-माई जोरे।
== संदर्भ ==
{{reflist}}
[[श्रेणी:जियत लोग]]
[[श्रेणी:भोजपुरी प्लेबैक सिंगर]]
{{Singer-stub}}
m7syo5nbfwhzdf41wp9mg5fn92pj3dt
803334
803333
2026-07-29T16:21:36Z
KiranBOT
38109
removed AMP tracking from URLs ([[:m:User:KiranBOT/AMP|details]]) ([[User talk:Usernamekiran|report error]]) v2.2.9s
803334
wikitext
text/x-wiki
{{Infobox musical artist
| name = राज मोहन
| native_name =
| image =
| caption = राज मोहन 2019 में
| image_size = 250
| background = सोलो गायक
| birth_name = राज मोहन<ref>{{cite web|url=https://www.thelallantop.com/videos/interview-of-raj-mohan-a-netherlands-based-bhojpuri-singer-musician-and-lyricist/|title=नीदरलैंड्स में रहे वाला सिंगर राज मोहन ले सुनी बिदेस में गावे वाला भोजपुरी गीत|publisher=thelallantop.com|}}</ref>
| birth_date = {{Birth date and age|1964|08|28|df=y}}
| birth_place = [[पारामारिबो]], [[सूरीनाम]]
| death_date =
| genre = पॉप , ग़ज़ल<ref>{{cite web|url=https://navbharattimes.indiatimes.com/metro/mumbai/other-news/raj-mohan-is-making-bhojpuri-songs-popular-in-suriname/articleshow/67220654.cms|title=राज मोहन : सूरीनाम में भोजपुरी गाना के प्रसिद्ध करत बाड़न|publisher=navbharattimes.com|}}</ref>
| occupation = ब्यास, गीतकार
| years_active = 1988–अभिले
| associated_acts =
| label =
| website =
}}
'''राजमोहन''' एगो डच [[भोजपुरी]] गायक हवन। ई सरनामी भोजपुरी मे ढेरे गीत लिखले आ गवले बाड़न।<ref>{{cite web|url=https://wap.business-standard.com/article-amp/pti-stories/raj-mohan-the-man-popularising-bhojpuri-songs-in-suriname-118122300506_1.html|title=राज मोहन: भोजपुरी गीत के सूरीनाम में लोकप्रिय करे वाला गायक|publisher=business-standard.com|4=|access-date=2020-09-01|archive-date=2018-12-23|archive-url=https://web.archive.org/web/20181223190159/https://wap.business-standard.com/article-amp/pti-stories/raj-mohan-the-man-popularising-bhojpuri-songs-in-suriname-118122300506_1.html|url-status=dead}}</ref> 2011 में इ आपन पहिला पॉप एल्बम रिलीज़ कइलन जेकरा में सरनामी-भोजपुरी बोल के हिंदी गाना अउरी कविता संगे मिलावाल रहे, सब इंकर लिखल अउरी संगीत दिहल रहे।<ref>{{cite web|url=https://hindi.timesnownews.com/amp/bollywood/bollywood/article/bhojpuri-singer-raj-mohan-wants-to-make-bhojpuri-music-in-every-genre-says-in-exclusive-interview/397006|title=मुश्किलों में संगीत के ना साथ छोड़ले, इ गायक नीदरलैंड में रहके भोजपुरी में पहचान बनवलन|publisher=hindi.timesnownews.com|4=|access-date=2020-09-01|archive-date=2019-04-09|archive-url=https://web.archive.org/web/20190409072303/https://hindi.timesnownews.com/amp/bollywood/bollywood/article/bhojpuri-singer-raj-mohan-wants-to-make-bhojpuri-music-in-every-genre-says-in-exclusive-interview/397006|url-status=dead}}</ref> इ पहिलका बेरी रहे सूरीनाम के इतिहास में की सरनामी-भोजपुरी के कंटेम्पररी के रूप में इस्तेमाल कइल गइल।<ref>{{cite web|url=https://scroll.in/article/743625/the-sounds-of-surinam-when-bhojpuri-creole-meets-carribbean-beats|title=सूरीनाम के संगीत: जब भोजपुरी-क्रियोल मिलल कैरेबियन ताल संगे|publisher=scroll.in|}}</ref> राज मोहन आपन एल्बम 'कंट्राकी (2005)' ले सरनामी-भोजपुरी गीत के इजात कइलन, गीत अउरी ग़ज़ल स्टाइल में, जेकर कुल्ह दुनिया में बड़ाई भइल।<ref>{{cite web|url=http://www.rajmohan.nl/|title=राजमोहन के जीवनी|3=|access-date=2020-09-01|archive-date=2020-09-24|archive-url=https://web.archive.org/web/20200924173814/http://www.rajmohan.nl/|url-status=dead}}</ref>
== शुरूआती जिनगी अउरी पेशा ==
राज सूरीनाम केें पंचवा-पीढ़ी बिहारी-भारतीय बाड़न, इंकर पुरखन के अंग्रेजन सूरीनाम में गिरमिटिया मजदूर बना के ले गइल रहन सन। इ अट्रेक्ट, नीदरलैंड में बड भइलन अउरी छोट उमिर में संगीत में रूचि देखऽवलन।<ref>{{cite web|url=https://zeenews.india.com/hindi/entertainment/bhojpuri/raj-mohan-popular-songs-are-made-in-bhojpuri-songs-in-suriname/482037/amp|title=बिदेस के धरती प भोजपुरी गीतन के प्रसिद्धि दिलावत बाड़े म्यूजिशियन राज मोहन|publisher=Zee News|access date=}}</ref>
12 के उमिर में राज [[सूरीनाम]] ले [[नीदरलैंड]] चल गइले अपना बाबु-माई जोरे।
== संदर्भ ==
{{reflist}}
[[श्रेणी:जियत लोग]]
[[श्रेणी:भोजपुरी प्लेबैक सिंगर]]
{{Singer-stub}}
h1mulj3arzccchlv8sen42zz9ymdt08
आरा हिले छपरा हीले बलिया हीलेला
0
76200
803317
729411
2026-07-29T16:20:28Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803317
wikitext
text/x-wiki
'''आरा हीले छपरा हीले बलिया हिलेला''' एगो [[भोजपुरी लोकगीत]] हऽ जेकरा ढेरे फिलिम आ एलबम मे फेर से बनावल गइल बा।<ref>{{Cite book|last=घोष|first=अविजित|title=सिनेमा भोजपुरी|publisher=पेंगुइन यू के|year=|isbn=|location=|pages=}}</ref>
== रूपांतरन ==
=== 1977 फिलिमी रूपांतरन ===
भोजपुरी फिलिम ''दंगल'' मे हेकरा फेर से बनावल गइल रहे आ हेकर लेखक रजपती रहन।<ref>{{Cite book|last=घोष|first=अविजीत|title=सिनेमा भोजपुरी|publisher=|year=|isbn=|location=|pages=}}</ref>
=== 1996 फिलिमी रूपांतरन ===
[[मिथुन चक्रवर्ती]] के हिंदी फिलिम ''अंगारा'' मे ई गाना के पुनर्निर्माण कइल गइल रहे।
=== 1996 फिलिमी रूपांतरन ===
बॉलीवुड फिलिम "अपने दम पर" मे एह गाना के पुनर्निर्माण भइल जेकरा [[गोविंदा]] गइलन।
=== 2012 फिलिमी रूपांतरन ===
भोजपुरी चलचित्र ''खून पसीना'' मे हेकर पुनर्निर्माण भइल।
=== 2019 फिलिमी रूपांतरन ===
सिद्धार्थ मल्होत्रा आ [[परिणीति चोपड़ा]] के चलचित्र ''जबरिया जोड़ी'' मे ई गाना के पुनर्निर्माण भइल।<ref>{{Cite news|last=|first=|date=|title=Jabariya Jodi: Parineeti Chopra, Sidharth Malhotra to launch song, UP Hile Zilla Hile, a remake of Bhojpuri song Ara Hile|work=हिंदुस्तान टाइमस|url=https://www.hindustantimes.com/bollywood/jabariya-jodi-parineeti-chopra-sidharth-malhotra-to-launch-song-up-hile-zilla-hile-a-remake-of-bhojpuri-song-ara-hile/story-moox4VJjXQFw5paUgg6GSK_amp.html|url-status=live|access-date=}}</ref>
=== 2020 रीमिक्स ===
भोजपुरी गायक [[रितेश पांडे]] एगो स्वतंत्र एल्बम मे एह गाना के फेर से बनवलन।<ref>{{Cite web|title=Bhojpuri Song: रितेश पांडे के नये गाने 'काशी हिले, छपरा हिले, पटना हिलेला'|url=https://www.prabhatkhabar.com/amp/story/entertainment%252Fritesh-pandey-new-bhojpuri-song-kashi-hile-chhapra-hile-goes-viral-watch-video|last=|first=|date=|website=|url-status=live|archive-url=|archive-date=|access-date=}}</ref>
अउरीयो सभ भोजपुरी फिलिम मे ई गाना बनल आ बहुत प्रख्यत भइल।
== सनरभ ==
[[श्रेणी:भोजपुरी लोकगीत]]
{{Poem-stub}}
pjei5bb9fjlzkd704ywo65de9i1zwkc
सृष्टिकर्ता
0
76227
803328
788948
2026-07-29T16:21:14Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803328
wikitext
text/x-wiki
'''सृष्टिकर्ता''' धर्म सभ मे एगो अइसन [[परमेसर]] के कहल जाला जे [[पृथिवी]] चाहे [[ब्रह्मांड]] के निर्माता होला। [[एक ईश्वर वाद]] मे ऊ अकेल परमेसर के सृष्टिकर्ता मानल जाला।
==एक ईश्वर वाद==
===अतेनत्व===
{{See also|अतेन धरम}}
अतेनत्व, नवका राज बजी [[अखेनातेन]] आ रानी [[नफ़्तिर्ती]] के १३३० ईशा पूर्व मे चालू कइल गइल। नीज आ उपसक लोग ला ऊ लोग नावा राजधानी बनवलस। ओकर पिता उनकर एक से बेसी ईश्वर वाला धरम मे आउर सभ भगवान संगे [[अतेन]] के पूजत रहन। फैरो के अंत के संगे संगे अतेनत्व ओ ओरा गइल। कुछु बिद्वानन के ई कहनाम बा की अतेनत्व, सभ से पुरान एक ईश्वर वादी धरम मे ले एगो बाटे।
===इब्राहिमी धरम===
इब्राहिमी उत्पत्ति के ब्योरा दू गो खिसा से बनल बाटे, जवन की [[आदि पुस्तक]] के पहिलका अध्याय से लमसम मिलेला। पहीलका खिसा, दैवीय उपचार आ पूर्ति के एगो दोहरावदार संरचना आ ई काहनाम "x वा दिन, सांझ आ भिनुसार रहे" उत्पत्ती के छव दिन मे हर दिन ला नियोजित करेला। पहिकला तीन दिन मे बखरा लागेला: पहिला दिन अन्हार आ अंजोर के बखरा लगावेला, दूसरका दिन ऊपर आ नीचे के पानी के, तीसरका दिन भुईं आ सागर के। अगिला तीन दिन सभ के बस्तू दिहल गइल, चउथका दिन अन्हार आ अंजोर के [[सुरुज]], [[चंद्रमा|चान]] आ [[तारा|जोन्ही]] दिहल गइल, पंचमा दिन सागर आ आकाश के मछ आ पाखी दिहल गइल। छवमा दिन भुईंया प मानुस सभ के उतारल गइल।
===सिख धरम===
सिख धर्म ओ सृष्टिकर्ता मे अस्था राखेला जेकरा ई [[वाहेगरू]] बोलेला। वाहेगरू नीरकर आउर अंतरधयान हवन।
[[श्रेणी:धर्म]]
{{Reli-stub}}
tr7fjh6uonaj3dlo6coxa8h4iq3vy6z
मोइन उल हक स्टेडियम
0
76249
803327
722308
2026-07-29T16:21:10Z
SM7Bot
7808
/* मैदान के जानकारी */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803327
wikitext
text/x-wiki
{{Infobox cricket ground
| ground_name = मोइन उल हक स्टेडियम
| nickname =
| image = Moin-Ul-Haque Stadium.JPG
| Type. = International Cricket Stadium
| caption =
| country = भारत
| location = [[पटना]], [[बिहार]], [[भारत]]
| coordinates = {{coord|25|36|28|N|85|10|4|E|type:landmark|display=it}}
| establishment = 1969
| seating_capacity = 25,000
| owner = [[बिहार क्रिकेट संघ]]
| operator = [[बिहार क्रिकेट संघ]]
|name after =
| tenants = भारतीय क्रिकेट टीम<br/>बिहार क्रिकेट टीम<br/>
| end1 = गंगा एंड
| end2 = पविलिअन एंड
| international = true
| firstodidate = 15 November,
| firstodiyear = 1993
| firstodihome = Sri Lanka
| firstodiaway = Zimbabwe
| lastodidate = 27 February,
| lastodiyear = 1996
| lastodihome = Zimbabwe
| lastodiaway = Kenya
| onlywtestdate = 17–19 November
| onlywtestyear = 1976
| onlywtesthome = India
| onlywtestaway = West Indies
| firstwodidate = 5 January
| firstwodiyear = 1978
| firstwodihome = India
| firstwodiaway = New Zealand
| lastwodidate = 22 December
| lastwodiyear = 1997
| lastwodihome = India
| lastwodiaway = South Africa
| date = 8 November
| year = 2019
| source = http://content-www.cricinfo.com/india/content/ground/58373.html Moin-Ul-Haque Stadium, Cricinfo
}}
'''मोइन उल हक स्टेडियम''' [[बिहार]] राज्य के [[पटना]] मे एगो स्टेडियम बाटे। एहमे तीन गो [[एकदिवसीय अंतराष्ट्रीय]] मैच भइल बा जेमे से एगो मैच 1996 के विश्व कप के रहे। एकर क्षमता 24000 दरसक के बाटे। ई बिहार रणजी दल के घरेलू मैदान हटे। बिहार सरकार अबहिं एकर बिकास कऽ के 42,500 के क्षमता पे काज करऽता। बिहार के क्रिकेट टीम एह मैदान प बहुते रंजी मैच खेलल बा।
==इतिहास==
पहिले एकर नांव राजेन्द्र नगर स्टेडियम रहे। 1970 मे मोइन उल हक के नांव प हेकर नांव धराइल। जेवन की भारतीय ओलींपिक संघ के मुख सचिव रहन आ बिहार क्रिकेट संघ के संस्थापक लोग मे से एगो रहन।<ref>{{cite web |url=http://content-usa.cricinfo.com/india/content/story/256671.html |title=Bihar Cricket Association reinstated |accessdate=2008-09-10 |url-status=live |archiveurl=https://web.archive.org/web/20080911171106/http://content-usa.cricinfo.com/india/content/story/256671.html |archivedate=11 September 2008 |df=dmy-all }}</ref>
==मैदान के जानकारी==
ई एगो आधुनिक आ अंतराष्ट्रीय स्तर के स्टेडियम रहे।<ref>{{cite web |url=http://beta.cricket.yahoo.com/cricket/profile/ground-profile?gid=98 |title=Moin-ul-Haq Stadium (Moinul Haque Stadium) |accessdate=2008-09-10 |url-status=dead |archiveurl=https://archive.is/20120707010310/http://beta.cricket.yahoo.com/cricket/profile/ground-profile?gid=98 |archivedate=7 July 2012 |df=dmy-all }}</ref> बाकिर भारतीय क्रिकेट नियंत्रण बोर्ड आ बिहार क्रिकेट संघ के लड़ाई के चलते एह स्टेडियम मे मैच के आजोजन ना होखला के चलते ई पुरान हो गइल आ हेकर हालत खराब भ गइल। [[झारखंड]] आ [[बिहार]] मे बखरा लगला के बादि बिहार क्रिकेट के मान्यता चल गइल आ एहिजा रणजी ओ होखल बन हो गइल। बाकिर फेर मान्यता मिलला के बादि एहिजा रणजी होला आ बिहार रणजी दल एहीजे मेजबान बनि के खेलेला। एह मैदान के अंतराष्ट्रीय मानक प तेयार कइल जाता।
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:पटना]]
[[श्रेणी:बिहार में खेलकूद]]
nnsfdi8tn2lo7q01s0o158pr8zhasl3
क्रिकेट बैट
0
76259
803318
693259
2026-07-29T16:20:32Z
SM7Bot
7808
/* बनाई */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803318
wikitext
text/x-wiki
[[File:A modern Cricket bat (back view).jpg|thumb|बैट कर एगो छबि]]
'''क्रिकेट बैट''' [[क्रिकेट]] मे बैट्समैन के प्रयोग मे आवे अला एगो समान बाटे। ई दू भाग मे होला, एक भाग गोल काठ के होला जेने से खेलत बजी धाइल जाला आ दोसरा भाग सपाट आ चउडा होला जेकर मदद से गेना मारल जाला। एकर मानक लंबाई ३८ इंच आ चउड़ई सावा चार ईंच होखेला। १९७९ मे बनल नियम के चलते बैट खाली काठे के बन सकेला अउरी कुछु के ना।
==बनाई==
[[File:Pod shaving of cricket bat by hands.jpg|thumb|क्रिकेट बैट के छिलाई]]
बैट के पटरा काठ के होला, जवन की जोन पटी से गेना के मारल जाला ओह पटी समतल रहेला आ दोसर ओरे तिरकोन लेखा उठल रहेला आ बीच मे बसी मोटहन होला। पारंपरिक रूप से बैट वीलो के काठ से बनेला, मुख रूप से वाइट वीलो ([[भोजपुरी]]: उज्जर वीलो) चाहे क्रिकेट बैट वीलो से बनावल जाला,जे विलो के एगो परकार होला। एहमे लाइनसीड के तेल पियावल जाला जेकरा से बैट हाली बाउर ना होखे। वाइट वीलो के परजोग एह से कइल जाला काहेकी ई बहुते हलुक आ बरियार होला।
बैट के पटरा (ब्लेड) एगो लाम हैंडल से जोड़ल रहेला। हैंडल प एगो रबर के ग्रिप लागल रहेला।
[[श्रेणी:क्रिकेट]]
{{Sport-stub}}
1bud43zs9wsn2kspxgf35kjuhokt13u
चंपारण मीट
0
76302
803305
799100
2026-07-29T16:07:12Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803305
wikitext
text/x-wiki
{{Infobox food
| name = चंपारण मीट
| image = Champaran meat.jpg
| image_size =
| image_alt =
| caption =
| alternate_name = अहुना, हांडी मीट, बटलोही
| type =
| course = मुख्य खाना
| country = [[भारत]]
| region = [[चंपारण]], [[बिहार]]
| national_cuisine =
| creator = <!-- or | creators = -->
| year =
| mintime =
| maxtime =
| served = Hot
| main_ingredient = चिकन, मटन आ भारतीय मसाला
| minor_ingredient = [[घीव]], [[सरसों क तेल]], [[दही]], [[प्याज]], [[अदरक]], [[सूखा फल]]
| variations = कईयन ठे
| serving_size = 100 ग्राम
| calories =
| calories_ref =
| protein =
| fat =
| carbohydrate =
| glycemic_index =
| similar_dish = मटन करी, चिकन करी
| cookbook =
| other =
}}
'''चंपारण मीट''', जेकरा के '''अहुना''', '''हांडी मीट''' चाहे '''बटलोही''' कहल जाला, [[बिहार]] के जिला [[चंपारण]] से निकले वाला पकवान हवे। मीट, मने की मांस, के सरसों के तेल अउरी घीव, लहसुन, पियाज, अदरक के मिलजुल पीसल मसाला के पेस्ट के संगे मैरीनेट कइल जाला। माटी क हांड़ी में पाके खाती चढ़ा दिहल जाला आ ओकरा बाद ओकर मुँह गूंथल आटा से नीक से सील कइल जाला। एकरा के लकड़ी के आग के मद्धिम आँच प लहे-लहे पकावल जाला आ खाना बनावे के दौरान बीच-बीच में हिलकोरल जाला। सवाद आ खाना बनावे के समय एह बात प निर्भर करे ला की मीट कईसन बाटे।
==संदर्भ==
{{Reflist}}
[[श्रेणी:बिहार के खाना]]
[[श्रेणी:चंपारण जिला]]
{{Food-stub}}
{{bihar-stub}}
3dftxxd2yagekjod65b4260tf83bygq
सुशांत सिंह राजपूत
0
76426
803326
729787
2026-07-29T16:21:06Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803326
wikitext
text/x-wiki
{{Infobox person
| name = सुशांत सिंह राजपूत
| image = Sushant Singh Rajput snapped at the promotions of 'M.S. Dhoni - The Untold Story' (cropped).jpg
| image_size = 270px
| caption = "एमएस धोनी: द अनटोल्ड" स्टोरी के परचार करत
| birth_date = {{birth date|1986|01|21|df=y}}<ref>{{cite web |title='Happy Birthday, Sushant Singh Rajput. Keep That Childlike Smile Always Alive,' Tweets Kriti Sanon |url=https://www.ndtv.com/entertainment/happy-birthday-sushant-singh-rajput-keep-that-childlike-smile-always-alive-tweets-kriti-sanon-1802788 |website=[[NDTV]] |author=Shruti Shiksha |date=21 January 2018 |accessdate=6 December 2018}}</ref>
| birth_place = [[पटना]], [[बिहार]], भारत<ref name="time_Madh"/>
| death_date = {{Death date and age|df=y|2020|06|14|1986|01|21}}<ref>{{Cite web |date=14 June 2020 |title=Sushant Singh Rajput commits suicide at Mumbai home |url=https://www.indiatoday.in/movies/celebrities/story/sushant-singh-rajput-commits-suicide-at-mumbai-home-1688886-2020-06-14 |access-date=14 June 2020 |website=[[India Today]]}}</ref>
| death_cause = फसरी लगाई के आत्महत्या<ref>{{Cite web |date=14 June 2020 |title=Cite web |date=14 June 2020 |title=Sushant Singh Rajput commits suicide at Mumbai home |url=https://www.indiatoday.in/movies/celebrities/story/sushant-singh-rajput-commits-suicide-at-mumbai-home-1688886-2020-06-14 |access-date=14 June 2020 |website=[[India Today]]}}</ref>
| death_place = [[मुंबई]], [[महाराष्ट्र]], भारत
| nationality = भारतीय
| occupation = {{hlist|अभिनेता|नर्तक}}
| years_active = 2008–20
| height =
| alma_mater =
}}
'''सुशांत सिंह राजपूत''' एगो भारतीय अभिनेता रहन। ई टीवी सीरियल सभ से आपन कैरियर चालू कइलन। हिनकर पहिला शो "किस देश में है मेरा दिल रहे"। ई "पवित्र रिश्ता" से बहुते प्रख्यात भइलन। हिनकर पहिलका फिलिम "काय पो छे" (2013) रहे।
सुशांत आपन फिल्म के शुरुआत 2013 मे काय पोछे से कइलन। एकरा बाद उह देसी रोमांस, पीके, डिटेक्टीव ब्योमकेश बक्शी में काम कइलन। 2016 के फिल्म जेकर नाव एम एस धोनी: द अनटोल्ड स्टोरी में ऊ महेंद्र सिंह धोनी के भूमिका कइले बाड़न। जेकरा खातिर उनकरा के खूब पसंद कइल गइल। अभिनय के आलावा ऊ अलग अलग कार्यक्रम जयसे की '''सुशांत4एजुकेशन''' में शामिल रहन। ऐकर आलावा उह कुछ प्रौद्योगिकी कंपनी के भी संस्थापक कइले रहन। 14 जून 2020 के सुशांत सिंह राजपूत के देह पंखा से लटकल मिलल, जेकर जांज केंद्रीय अन्वेषण ब्यूरो द्वारा कइल जाता।
== जिनगी ==
हिनकर जनम पटना मे भइल बाकिर हीनकर गाँव [[पूर्णिया]] बाटे। हिनकर एगो बहिनी, मितु सिंह, राज्य स्तर के किरकेटर बाड़ी। हिनकर माई के देहांत 2002 मे भ गइल रहे, जेकरा से हिनका बहुत बड़ सदमा लागल रहे आ ओही बरिस हीनकर परिवार दिल्ली चलि गइल।
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:1986 में जनम]]
[[श्रेणी:2020 में निधन]]
[[श्रेणी:हिंदी फिलिम एक्टर]]
lsxubrg30obvthn171ndlxt83nwxd6t
वुमन पावर लाइन 1090
0
76715
803320
781508
2026-07-29T16:20:41Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803320
wikitext
text/x-wiki
'''''वूमेन पावर लाइन''''' ({{Lang|en|Women Power Line}}) [[उत्तर प्रदेश पुलिस]] क भारतीय समाज के परेशान करे वाली ई असामयिक समस्या: यौन उत्पीड़न, और एकर जड़ से हमरे समाज क पितृसत्तात्मक मानसिकता क गलतफहमी क विरुद्ध चलावे जाये वाला अभियान बा।<ref>{{Cite web|url=http://uphome.gov.in/wpl-hi.htm|title=वूमेन पावर लाइन|website=गृह विभाग, उत्तर प्रदेश सरकार|archive-url=https://web.archive.org/web/20191221030407/http://uphome.gov.in/wpl-hi.htm|archive-date=21 दिसंबर 2019|access-date=26 जून 2020}}</ref> ई महीलन क सुरक्षा खातिर काम करे वाला सरकारी कॉल सेंटर बा। ई 24x7 काम करे ला।
==इतिहास==
उत्तर प्रदेश में महीलन क सुरक्षा अऊर उनकरे में जागरूकता लावे खातिर औउर महिला उत्पीड़न क घटना के सख्ती से रोके खातिर उत्तर प्रदेश क अखिलेश यादव सरकार 2012 में वुमन पावर लाइन 1090 क स्थापना कइले ।
वूमेन पावर लाइन 1090 सेवा के अऊर मजबूत बनावे खातिर 2014 में "वुमेन सिक्योरिटी ऍप 1090" (महिला सुरक्षा ऍप १०९०) सेवा भी प्रारंभ कइल गइल बा ।
==संदर्भ==
{{reflist}}
[[श्रेणी:उत्तर प्रदेश में शिक्षा]]
[[श्रेणी:नारीवाद]]
{{UP-stub}}
s1dkfcs7cc06httpz4uzn9v06j8mh65
भारतीय संविधान के आमुख
0
76756
803360
775393
2026-07-29T16:23:33Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803360
wikitext
text/x-wiki
[[File:Preamble to the India Constitution Bhojpuri.png|thumb|250px|भारतीय संविधान के आमुख]]
'''भारतीय संविधान के आमुख''' एगो नन्हीचुक्की चिन्हात्मक कहनाम बाटे। ई देशवासीयन के राह देखावेला, आ संविधान के सिद्धांतन के प्रस्तुत करेला। एकरा के [[संविधान सभा]] द्वारा 26 नवम्बर 1949 के स्विकारल गइल आ 26 जनवरी 1950 के भारत के [[गणतंत्र दिवस (भारत)|गणतंत्र दिवस]] के रूप में मनावल गइल।
।<ref>{{cite book|last1=Baruah|first1=Aparijita|title=Preamble of the Constitution of India: An Insight and Comparison with Other Constitutions|date=2006 |publisher=Deep & Deep|location=New Delhi|isbn=978-81-7629-996-1|page=177|url=https://books.google.com/?id=aPujAJ1UVqUC&pg=PA177#v=onepage&q&f=true|accessdate=12 November 2015}}</ref> एहमे प्रजा के आस अउर अकांक्षा के वर्णन भेटाला। <ref>[http://legislative.gov.in/constitution-forty-second-amendment-act-1976 42th Amendment]</ref> आमुख के भारतीय संविधान के आत्मा कहल जाला।
==लेखा==
[[देवनागरी]] में;
{{cquote|हमनि, भारतीय प्रजा, भारत के एगो सुसंपन्न <br/>परमाधिपत्यक, सामाजवादी, धम्मनिरपेक्ख, लोकतनंत्रातमक,<br/>गणराज्य के रूप में स्थापित करे खातिर <br/>तथा एकर सगरी नागरिकन के;<br/>सामाजिक, आर्थिक, आ राजनीतिक न्याय;<br/> बिचार, अभिव्यक्ति, बिश्वास, धर्म<br/>आ आराधना के स्वतंत्रता;<br/>आदर आ तक के समानता प्राप्त करावे खातीर<br/>तथा ई सौसे प्रजा मे, व्यक्ति के गौरव आ राष्ट्र के एकता <br/>आ अखण्डिता, निर्धारित करावे ऐसन <br/>बन्धुत्ता के बिक्सावेला, <br/>गंभीरतापूर्ण संकल्प कके आपन संविधान सभा मे आज 26 नवंबर 1949 (अगहन सुद शुक्ल सप्तमी विक्रम संवत दु हजार छव) के रोज <br/> एहसे ई संविधान के स्वीकार कके, एह के अधिनियमित आ आत्मसमर्पित करऽतानीजा।}}
{{clear}}
==ऐतिहसिक पूर्वचरितम्==
[[चित्र:Emblem_of_India_(without_motto).svg|भारत के प्रतीक चिन्हा|alt=Emblem of India|thumb]]
आमुख उद्देश्य प्रस्ताव पर आधारित बा जेके 13 दिसंबर 1946 के संविधान सभा में
[[जवाहरलाल नेहरू]] द्वारा प्रस्तुत कइल गइल जे 22 जनवरी 1947 के सकारऽल गइल आ संविधान सभा द्वारा 26 नवंबर 1949 के अपनावल गइल आ 26 जनवरी 1950 से लागू भइल। [[भीमराव आंबेडकर|बी.आर. आम्बेडकर]] आमुख के लेके कहले के;
<blockquote> ''साच्चो में, ई जिनगी के एगो ऐसन डगर रहे, जऽवन स्वतंत्रता, समानता आ बन्धुत्ता के जिनगी के सिद्धांत के रूप में चिन्हऽत रहे आ जेकरा के एक दोसरा से अलगऽ ना कइल जा सकेला: स्वतंत्रता के समानता से अलग ना कइल जा सकेला; समानता के स्वतंत्रता से अलग ना कइल जा सके। ना तऽ स्वतंत्रता आ समानता के बन्धुत्ता से अलग कइल जा सके। बिना समानता के स्वतंत्रता से बहुत लोगीन पर तिनका लोगीन के वर्चस्व पैदा होला। बिना स्वतंत्रता के समानता व्यक्तिगत पहल के मार देवेला। बिना बन्धुत्ता के स्वतंत्रता आ समानता स्वाभाविक तरीका से ना बन सकेला।'' </blockquote>
जब [[संविधान सभा]] एह आमुख पर बहस करत रहे तब ई राय दिहल गइल के भारत के नाँव बदल के "भारतीय समाजवादी गणराज्य संघ" रखल जाय, मानो के भारत [[सोवियत यूनियन|सोवियत संघ]] के नकल करत होखे, यद्दपि अन्य सदस्य एह बात से असहमत रहले।
ईहो बात पर बहस भइल के आमुख में भगवान आ गाँधी नाम शामिल कइल जाव के ना। पहिलका के मतदान तब हो गइल जब 68 गो सदस्य ‘भगवान’ के खिलाफ वोट दिहले। एच.वी.कामथे बेताब होके अभिप्राय दिल्ली के, ‘महासये, ई हमनी के इतिहास के एगो करिया दिन बा। भगवान भारत के बचावस।' जबके बाद मे - गाँधी के नाम शामिल करे के सुझाव [[ब्रजेश्वर प्रसाद]] नाकार दिहले, जिन्करा लागऽत रहे के 'सड़ल संविधान' - जवन अमेरिकी सुप्रीम कोर्ट के मामला आ भारत सरकार के कानून पर आधारित बा, एह मे 'गाँधीवादी' प्रकृति नइखे, से गाँधी के नाँद शामिल ना होखे। प्रसाद कहले के, "हम नईखी चाहत कि [[महात्मा गांधी]] के नाँव एह संविधान में शामिल होखे, काहेंसेके इ गांधीवादी संविधान नइखे। जदी हमनी के लगे गांधीवादी संविधान रहित तऽ हम पहिलका आदमी रहती जवन आपन समर्थन देती। गाँधी के नाम फेरा लेवे के चाहीं।"
[[भारत के सुप्रीम कोर्ट|भारतीय सर्वोच्च न्यायालय]] मूर रूप से बेरुबारी मामला में [[राष्ट्रपति]] संदर्भ में फैसला कइले रहे के आमुख भारतीय संविधान के अविभाज्य अङ नइखे, आ एही चल्ते ई अदालतन में लागू ना हो सकेला। यद्दपि उहे अदालत 1973 के केशवानन्द मामला में पहिले के फैसला के नामंजूर क देलस आ मान लेलस के आमुख के उपयोग संविधान के अस्पष्ट क्षेत्रन के व्याख्या करे खातीर कइल जा सकेला, जहवा अलग-अलग व्याख्या खुदे के प्रस्तुत करेले। 1995 में केंद्र सरकार बिरुद्ध भारतीय एल.आई.सी के मामला में सुप्रीम कोर्ट एक बेरी फेरु से कहऽलस कि आमुख संविधान के अविभाज्य अङ बा।
आमुख जे के मूर रूप से निर्माणल गइल रहे,ओह मे राज्य के परमाधिपत्यक, लोकतांत्रिक, गणतंत्र के रूप में बतावल गइल रहे, बाकीर बाद में 42वा संशोधन में [[इमरजेंसी (भारत)|आपातकाल]] के दौरान [[संसद]] द्वारा धम्मनिरपेख आ समाजवादी शब्द पारित कइल गइल।
आमुखवाला पन्ना के सङे अस्सल [[भारतीय संविधान]] के बाकियो पन्ना के डिजाइन आ सजावट [[जबलपुर]] के मशहूर चित्रकार [[बेवहर राममनोहर सिन्हा]] कइले रहलें जे तब [[आचार्य नन्दलाल बोस]] के सङे [[शांतिनिकेतन]] में रहत रहलें। नन्दलाल बोस बगेर कवनो बदलाव कइले बेवहर राममनोहर सिन्हा के कलाकृति के समर्थन कइले। एह पन्ना के निचला दाहिनवारी कोना में [[देवनागरी लिपि]] में बेवहर राममनोहर सिन्हा के नन्हिचुक्की हस्ताक्षर "राम" बा। सुलेख [[प्रेम बिहारी नारायण रायजादा]] कइले रहले।
आमुख में 18 दिसंबर 1976 के मात्र एक्के बेरीया संशोधन भइल। भारत में आपातकाल के बेरा [[इंदिरा गाँधी]] सरकार संविधान के 42वा संशोधन में केतनहे परिवर्तन कइलस। एह संशोधन से "परमाधिपत्यक" आ "लोकतांत्रिक" शब्दन के बीच "समाजवादी" आ "धम्मनिरपेख" शब्द जोड़ल गइल आ "राष्ट्र के एकता" शब्द के परिवर्तित कके "राष्ट्र के एकता आ अखण्डित्ता" धऽ दिहल गइल।
साल 1994 में "एस.आर.बोमाई बिरुद्ध भारत संघ" मामला बेरा भारतीय सर्वोच्च न्यायालय के कहऽनामा रहे के [[धम्मनिरपेखता]] मुर संरचना सिद्धांत के हिस्सा बाटे।
==वचनऽबोधऽ==
===परमाधिपत्यक ===
परमाधिपत्यक के मतलब होला कवनो देश के स्वतंत्र अधिकार। माने ओकरा लगे कवनो विषय पर कानून बनावे के अधिकार बा; आ ई कवनो दोसरा देश / बाहरी शक्ति के नियंत्रण में नइखे। परम माने सर्वोच्च आ आधिपत्यक माने अधिकार रखऽनिहार से परमाधिपत्यक के शाब्दिक अर्थ जेकर अधिकार सर्वोच्च बा आमुख अनुसार भारत के अधिकार।
आमुख अनुसार भारतीय संविधान भारत के एगो [[संप्रभुता|परमाधिपत्यक]], लोकतांत्रिक, [[रिपब्लिक|गणतंत्र]] के रूप में स्थापित करेला आ आमुख में उल्लेखित सुपरिभाषित वचनन के सुरक्षित करेला भारत के जनता गंभीर संकल्प के अनुसरण करता। परमाधिपत्यक से तात्पर्य सर्वोच्च आ अंतिम शक्ति से बा। ई वास्तविक, साधारण, कानूनी, राजनीतिक, व्यक्तिगत भा बहुवचनीय हो सकेला। राजतंत्र में परमाधिपत्यकता राजा लोग के व्यक्तियन के दिहल जात रहे।
बाकिर वर्तमान दुनिया में अधिकतर गणतंत्रवादी सरकारन में परमाधिपत्यकता जनऽता के चुनल प्रतिनिधियन के हस्तांतरित होला।
===समाजवादी===
1976 मे 42वा संशोधन द्वारा ई शब्द के जोड़े से पहिले संविधान में राज्य नीति के निर्देशात्मक सिद्धांत के रूप में समाजवादी सामग्री रहे। इहावा उपयोग कइल गइल समाजवादी शब्द लोकतांत्रिक [[समाजवाद]] के संदर्भ देला, मने के लोकतांत्रिक, विकासवादी आ अहिंसक माध्यम से समाजवादी लक्ष्य के प्राप्ति। अनिवार्य रूप से एकर मतलब ई बा के धन के बितरण न्याय के माध्यम से समाज द्वारा समान रूप से कइल जाय (काहेसेके धन सामाजिक रूप से पैदा होला), तनीके लोगीन के हान में सहेजल ना रह जाय आ सरकार के सामाजिक-आर्थिक असमानता के काम करे खातिर जमीन आ उद्योग के स्वामीहक के नियंत्रित करे के चाहीं।
===धम्मनिरपेख===
धम्मनिरपेख माने के सरकार आ धम्मसमूहं के बीच के संबंध संविधान आ कायदा के अनुसार निर्धारित होखेला। ई राज्यशक्ति आ धम्मशक्ति के अलगा कर देला। 18 दिसंबर 1976 के 42वा संशोधनले "धम्मनिरपेख" शब्दो के आमुख में सहेज लेवल गइल। कवनो धम्म मे अंतर नइखे माने [[हिन्दू]], [[बौद्ध]], [[जैन]], [[सिख धर्म|सिख]], [[ईसाई धर्म|ईसाई]] आ [[इस्लाम]] सामान रुप से आदरभावीत बा आ एह से बेसी ई कवनो राज्य के राजधर्म नइखे। भारतीय नागरिकन के आपन धम्म के उपदेश, पालन, आराधना आ प्रसार करेके अनुमति बा। भारत द्वारा साकारल धम्मनिरपेख के अर्थ बतावते अलेक्जेंडर ओविक्स लिखऽले बाड़न के "[[धम्मनिरपेखता|धम्मनिरपेख]], भारतीय संविधान के मूरभुत संरचना के हिस्सा बाटे आ एकर मतलब होला सभे [[धर्म|धम्मन]] के सामान आजादी आ आदर।"
===लोकतंत्र===
[[भारत के लोग|भारत के जनता]] अपना सरकार के "सर्वत्र वयस्क मताधिकार व्यवस्था" से चुनेले, जवना के "एगो बेकत एगो मत" के नाँव से चिन्हऽल जाला। सरकार के ई प्रतिनिधि रूप देश के शासन खातिर उपयुक्त बा काहेसेके एकर जनसंख्या बहुत बड़ आ विविधतापरिपूर्ण बा। भारत के प्रत्येक नागरिक 18 साल आ ओकरा से बेसी उमिर के आ कायदा से प्रतिबन्धित ना होखे ऐसन मनुष मतदान के अधिकारी बाडे़। मोहन लाल त्रिपाठी बिरुद्ध जिला मजिस्ट्रेट मामला में "[[लोकतंत्र]]" शब्द पऽ विचारणा भइल आ कहल गइल के;
{| class= "wikitable"
|+
!"लोकतंत्र राजनीतिक तत्व के एगो बिचार बाटे, ई एगो आदर्श बा जेकर अभ्यास सांस्कृतिक रुप से समृद्ध आ राजनीतिक रूप से परिपक्व राष्ट्रन द्वारा लोगीन के प्रतिनिधीयन द्वारा शासन के साहारा ले के प्रत्यक्ष आ परोक्ष रूप से करऽल जाला।"
|}
लोकतंत्र शब्द खाली राजनीतिक लोकतंत्र के ना बलुक सामाजिक आ आर्थिक लोकतंत्रो के कहल जाला। लोकतंत्र के शामिल करे के मुख्य कारण ई बाटे के, लोगीन के आपन प्रतिनिधि चुने के आजादी मिल सके आ अत्याचारियन से देश के बचावल जा सके।
===गणतंत्र===
सरकार के गणतंत्रीय स्वरुप में राज्यप्रमुख के चुनऽल जाला ना के वंशानुगत राजकुमार के राज्यप्रमुख बनावऽल जाला। एह तरीका से ई शब्द ऐसन सरकार के बोध करावे ला जहावा केहू के लगे सार्वजनिकशक्ति , स्वामीत्वाधिकार के रूप मे ना होखे। लोकतंत्र के बिपरीत राजतंत्र, जवऽना में राज्य के प्रमुख के नियुक्ति आजीवन आ राजत्याग तक ले वंशानुगत आधार पर कइल जाला, लोकतांत्रिक गणतंत्र ऐसन एकम हवे जवऽना में राज्य के प्रमुख के सहऽजे आ परोक्ष रूप से एगो निर्धारित कार्यकाल खातिर चुनल जाला। एही तरह से भारत में [[राष्ट्रपति]] होला जे अप्रत्यक्ष रूप से चुनल जाला आ ओकर कार्यकाल निर्धारित होला। ईहवा विशेषाधिकार प्राप्त वर्ग के अभाव बा आ सगरी सार्वजनिक कार्यालय प्रत्येक नागरिक खातिर बगेर कवनो भेदभाव के खुलल बा।
===न्याय===
न्याय, कानून के राज, अत्याचार के किल्लत आ समाज में सभका खातिर समान अधिकार, स्वतंत्रता आ तक पऽ खाडा़ बा ।
भारत अपना सबितन मे समानता सुनिश्चित करे खातिर सामाजिक, आर्थिक आ राजनीतिक न्याय के खोज-बिन करेला।
( i ) सामाजिक न्याय
सामाजिक न्याय के मतलब होला समाज में सामाजिक विशेषाधिकार प्राप्त वर्ग के अभाव आ कवनो नागरिक के सङे जाति, धरऽम, रङ, लिंङ आ जनमस्थान के आधार पर भेदभाव ना कइल। भारत समाज से हर किसिम के शोषण के खतम करे खातिर खड़ा बा।
( ii ) आर्थिक न्याय
आर्थिक न्याय के मतलब होला आवक, संपऽति आ आर्थिक स्थिति के आधार पर मरद मेहरारू के बीच कवनो भेदभाव ना कइल। ई संपऽति के समान रूप से वितरण, आर्थिक समानता, उत्पादन आ वितरण के साधन सभ पर एकाधिकार नियंत्रण के अन्त, आर्थिक संसाधनन के विकेंद्रीकरण, आ सभके रोजी-रोटी के कमाई खातिर पर्याप्त मोका के सुनिश्चित करे खातिर खड़ा बा।
( iii ) राजनीतिक न्याय
राजनीतिक न्याय के मतलब होला जनता के राजनीतिक प्रक्रिया में भागीदारी खातिर समान, स्वतंत्र आ निष्पक्ष अवसर। ई बगेर भेदभाव के सगरी जनता के बरोबर राजनीतिक अधिकार देवे खातिर खड़ा बा। [[भारत के संबिधान|भारतीय संविधान]] में उदारवादी लोकतंत्र के प्रावधान बा जवना में सभ जनता के अधिकार बा, आ भाग लेवे के आजादीओ बा।
===स्वतंत्रता===
स्वतंत्रता के अवधारणा भारतीय नागरिकन के काराई पर आजादी के संदर्भ देला। एहमे ई स्थापित कइल गइल बा के भारतीय नागरिकन पर कवनो बेवजह रोक नइखे, के ऊ लोग का सोचत बाडे़, ओह लोग के अभिव्यक्ति के तरीका, आ कवना तरह से ऊ लोग आपन विचारन के काम में आगे बढ़ावल चाहत बाडे़, यद्दपी आजादी के मतलब ई नइखे कि कुछो करे के आजादी बा। एकर प्रयोग संवैधानिक हद में करे के पड़ी।
===समानता ===
समानता शब्द के मतलब होला समाज के कवनो वर्ग खातिर विशेषाधिकार के अभाव, आ बिना भेदभाव के सभ बेकत के पर्याप्त अवसर उपलब्ध करावल।
===बन्धुत्ता===
ई भाई-बहिन, आ मित्रता के भाव आ राष्ट्र से अप्नाहट के भाव के बोध करावेला।
आमुख में घोषणा कइल गइल बा के बन्धुत्ता के दूगो चीज के सुनिश्चित कराने के पड़ी- व्यक्ति के गौरव आ राष्ट्र के एकता आ अखण्डिता। आमुख में 42वा संविधान संशोधन (1976) से ‘अखण्डिता’ शब्द जोड़ल गइल।
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:भारत के संविधान]]
{{India-law-stub}}
6st7xwo599s03iuled2pcuf17jjzrxj
टीका (वैक्सीन)
0
76774
803337
781509
2026-07-29T16:21:48Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803337
wikitext
text/x-wiki
{{about|रोग निवारक दवाई, वैक्सीन|माथ पर लगावल जाए वाला टीका|टीका||टीका (बहुअर्थी)}}
{{Infobox medical intervention
| name = Vaccine
| image = Smallpox vaccine.jpg
| caption = [[Smallpox vaccine]] and equipment for administering it
| alt =
| pronounce =
| synonyms =
| ICD10 =
| ICD9 =
| ICD9unlinked =
| MeshID = D014612
| LOINC =
| other_codes =
| MedlinePlus =
| eMedicine =
}}
'''टीका''' चाहे '''भैक्सिन''' ({{Lang|en|Vaccine}}, ''वैक्सीन'') एगो अइसन जैविक तेयारी बाटे जे केवनो संक्रामक रोग से लड़े के सकान देवेला। एगो निसचित रोग के टीका के उहे रोग फइलावे अला जीव के तनी अबर कऽ के चाहे मुआ चाहे ओकरे कोनो भाग से बनावल जाला। टीका मे जोन अबर कइल चाहे मुआइल जीव रहेला, जब देहि मे जाला तऽ देह ओकरा के चिन्ह जाला आ फेर जब ओह रोग के रूप मे असल जीव देहि मे आवेला देहि होकरा से लड़े के सकान राखेला आ ऊ रोग बेकत के ना होखेला।<ref name="pmid26214521">{{cite journal | vauthors = Melief CJ, van Hall T, Arens R, Ossendorp F, van der Burg SH | title = Therapeutic cancer vaccines | journal = The Journal of Clinical Investigation | volume = 125 | issue = 9 | pages = 3401–12 | date = September 2015 | pmid = 26214521 | pmc = 4588240 | doi = 10.1172/JCI80009 }}</ref><ref name="pmid26861670">{{cite journal | vauthors = Bol KF, Aarntzen EH, Pots JM, Olde Nordkamp MA, van de Rakt MW, Scharenborg NM, de Boer AJ, van Oorschot TG, Croockewit SA, Blokx WA, Oyen WJ, Boerman OC, Mus RD, van Rossum MM, van der Graaf CA, Punt CJ, Adema GJ, Figdor CG, de Vries IJ, Schreibelt G | title = Prophylactic vaccines are potent activators of monocyte-derived dendritic cells and drive effective anti-tumor responses in melanoma patients at the cost of toxicity | journal = Cancer Immunology, Immunotherapy | volume = 65 | issue = 3 | pages = 327–39 | date = March 2016 | pmid = 26861670 | pmc = 4779136 | doi = 10.1007/s00262-016-1796-7 }}</ref><ref>{{cite journal |vauthors=Brotherton J |title=HPV prophylactic vaccines: lessons learned from 10 years experience |journal= Future Virology|volume=10 |issue=8 |pages=999–1009 |year=2015 |pmid= |doi=10.2217/fvl.15.60 }}</ref><ref name="pmid24748633">{{cite journal | vauthors = Frazer IH | title = Development and implementation of papillomavirus prophylactic vaccines | journal = Journal of Immunology | volume = 192 | issue = 9 | pages = 4007–11 | date = May 2014 | pmid = 24748633 | doi = 10.4049/jimmunol.1490012 | doi-access = free }}</ref>
टीका लगावे के काज के टीकाकरन कहल जाला। छुआवे ठेकाए से होखे अला रोग के रोके ला टीकाकरन सभ से नीमन पाय हऽ। टीकाकरन से चेचक लेखा रोग के दुनिया भ मे खतम कऽ दिहल गइल बा आ पोलियो, [[खसरा रोग|खसरा]] आ टेटनस जस रोग के कम कइल गइल बाटे। हेकरा [[अंग्रेजी]] मे ''भैक्सिन'' कहल जाला।
==असर==
बैग्यानिक लोग इ मानेला जे संक्रामक रोग के नस्ट करे ला टीका बहुते कारगर पाए हऽ।<ref>{{cite journal | vauthors = Orenstein WA, Bernier RH, Dondero TJ, Hinman AR, Marks JS, Bart KJ, Sirotkin B | title = Field evaluation of vaccine efficacy | journal = Bulletin of the World Health Organization | volume = 63 | issue = 6 | pages = 1055–68 | date = 1985 | pmid = 3879673 | pmc = 2536484 }}</ref><ref>{{Cite web|url=https://hub.jhu.edu/2017/01/11/vaccines-autism-public-health-expert/|title=The science is clear: Vaccines are safe, effective, and do not cause autism|last=Jan 11|first=Hub staff report / Published|last2=2017|date=2017-01-11|website=The Hub|access-date=2019-04-16}}</ref><ref>{{cite journal | vauthors = Ellenberg SS, Chen RT | title = The complicated task of monitoring vaccine safety | journal = Public Health Reports | volume = 112 | issue = 1 | pages = 10–20; discussion 21 | date = 1997 | pmid = 9018282 | pmc = 1381831 }}</ref><ref>{{Cite web|url=http://www.healthychildren.org/English/safety-prevention/immunizations/Pages/Vaccine-Safety-The-Facts.aspx|title=Vaccine Safety: The Facts|website=HealthyChildren.org|access-date=2019-04-16}}</ref> तबो हेकर परभाव के सीमा बाटे।
{{clear}}
== संदर्भ ==
{{Reflist|33em}}
== बाहरी कड़ी ==
{{external media
| align = right
| width = 300px
| video1 = [https://www.youtube.com/watch?v=mq-PgsVY-VU Modern Vaccine and Adjuvant Production and Characterization], [[Gen. Eng. Biotechnol. News|Genetic Engineering & Biotechnology News]]
}}
* {{curlie|Health/Pharmacy/Drugs_and_Medications/Vaccines_and_Antisera|Vaccines and Antisera}}
* [http://www.emro.who.int/entity/vpi/ WHO Vaccine preventable diseases and immunization]
* [https://web.archive.org/web/20070615154213/http://www.who.int/immunization/documents/positionpapers/en/ World Health Organization position papers on vaccines]
* [http://www.historyofvaccines.org/ The History of Vaccines], from the [[College of Physicians of Philadelphia]]
*: This website was highlighted by ''Genetic Engineering & Biotechnology News'' in its "Best of the Web" section in January 2015. See: {{small|{{cite news | title=The History of Vaccines | department=Best of the Web | volume=35 | issue=2 | date=15 January 2015 | page=38 | work=[[Gen. Eng. Biotechnol. News|Genetic Engineering & Biotechnology News]] }}}}
[[श्रेणी:वैक्सीन]]
[[श्रेणी:मेडिकल रोकथाम]]
76vn71dg65owhnp8ab077om8wfcgoev
बफेलो बफेलो बफेलो बफेलो बफेलो बफेलो बफेलो बफेलो
0
76878
803311
754425
2026-07-29T16:20:02Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803311
wikitext
text/x-wiki
{{italic title}}
'''''बफेलो बफेलो बफेलो बफेलो बफेलो बफेलो बफेलो बफेलो''''' अमेरीकी अंग्रेजी के एगो व्याकरन के दृष्टिकोन से सुध वाक्य हऽ।
एहमे बफेलो सबद के तीनगो माने हऽ:
* एगो संग्या सबद, जेवन अमेरिका के एगो नगर के नांव हटे।
* एगो क्रिया, जेकर माने केहू के तंग कइल चाहे हड़काइल होखेला।
* एगो जीव, जेकरा [[भोजपुरी]] मे भंइस कहल जाला।
==माने==
एह वाक्य के चारि भाग मे बुझल जा सकेला:
* बफेलो बफेलो: बफेलो नगर के बफेलो (भंइस)
* बफेलो बफेलो बफेलो: बफेलो नगर के बफेलो (भंइस) मे हड़कावत बा।
* बफेलो: हड़कावत बा
* बफेलो बफेलो: बफेलो नगर के बफेलो (भंइस) के
एकर पूरा माने इ भइल कि:
बफेलो नगर के बफेलो (भंइस) के बफेलो नगर के बफेलो (भंइस) हड़कावत बा जे उहो बफेलो नगर के बफेलो (भंइस) के हड़कावस।
आउर सरल सबद मे:
अमेरिका के बफेलो नगर के भंइसा ओही नगर के एगो अउर भइसा के बाकि सभ भईसा के हड़कावे के कहऽता।
[[श्रेणी:अंग्रेजी]]
{{grammar-stub}}
oblk5iq9khmermhacubfb0dvh95thib
बनारस मेट्रो
0
77431
803307
734425
2026-07-29T16:19:48Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803307
wikitext
text/x-wiki
{{Infobox Public transit
| name = बनारस मेट्रो
| image =
| imagesize =
| native_name = <!-- [[WP:NOINDICSCRIPT]] -->
| locale = [[बनारस]], [[उत्तर प्रदेश]], [[भारत]]
| transit_type = रैपिड ट्रांजिट
| began_operation =
| system_length = {{convert|29.235|km|mi|abbr=on}}
| chief_executive =
| headquarters =
| lines = 2 (प्रस्तावित)
| stations = 26 (प्रस्तावित)
| website =
| ridership =
| train_length = 3 कोच
| track_gauge =
| el =
| average_speed = 35 की.मी/प्रति घंटा
| top_speed = 90 की.मी/प्रति घटा
| owner = उत्तर प्रदेश मेट्रो रेल निगम
| operator =
| map =
| alt =
| caption =
| area served =
| line_number =
| start =
| end =
| annual_ridership =
| character =
| vehicles =
| map_state =
}}
'''बनारस मेट्रो''' एगो प्रस्तावित रैपिड ट्रांजिट सिस्टम बाटे [[बनारस]], [[उत्तर प्रदेश]] सहर खातिर। प्रस्तावित सिस्टम में दुगो कॉरिडोर भेल (तरना, शिवपुर) ले बनारस हिंदू विश्वविद्यालय तक (19.35 किमी) अउर बेनिया बाघ ले सारनाथ तक (9.885 किमी)। इ परियोजना के संभाव्यता अध्ययन राइट्स कइले रहे अउरी 2015 में पूरा कइलस।
एकरा में 26 गो स्टेशन सामिल कइल गइल बा, जेमे 20 गो अंडरग्राउंड रही अउर 6 गो उचाई प रही। दुनो कॉरिडोर के लंबाई 29.235 किमी, जेमे 23.467 किमी अंडरग्राउंड रही आ 5.768 किमी उचाई प रही, राज्य सरकार 150 करोड़ रोपया एकरा खातिर दे चुकल बिया फरवरी, 2019 में राज्य के मेट्रो परियोजना खातिर।<ref>{{cite web|url=http://timesofindia.indiatimes.com/city/varanasi/DPR-of-Varanasi-Metro-Rail-Project-approved/articleshow/52526623.cms|title=बनारस मेट्रो रेल परियोजना के डी पी आर मंजूर हो गइल - टाइम्स ऑफ इंडिया|publisher=|accessdate=24 October 2016}}</ref><ref>{{cite web|url=https://www.metrorailnews.in/tag/varanasi-metro-rail-project/|title=बनारस मेट्रो रेल परियोजना - - मेट्रो रेल खबर|publisher=|accessdate=24 October 2016}}</ref><ref>[http://www.business-standard.com/article/economy-policy/kanpur-varanasi-metro-rail-projects-could-start-by-december-2016-116021900701_1.html Kanpur, Varanasi metro rail projects could start by December 2016]</ref><ref>{{cite web|url=https://www.metrorailnews.in/varanasi-metro-centre-approves-varanasi-metro-rail-project-plan/|title=बनारस मेट्रो - केंद्र बनारस के मेट्रो रेल परियोजना मंजूर कइलस - - मेट्रो रेल खबर|publisher=|accessdate=24 October 2016}}</ref>
==इतिहास==
उत्तर प्रदेश सरकार [[बनारस]], [[लखनऊ]], [[मेरठ]] आ [[आगरा]] मे मेट्रो चालू करे के बिचरलस।<ref>{{cite web|url=https://www.metrorailnews.in/varanasi-metro-metro-rail-project-awaits-central-govts-approval/|title=Varanasi Metro - Metro rail project awaits central govt's approval - - Metro Rail News|publisher=|accessdate=24 October 2016}}</ref><ref>{{cite web|url=http://timesofindia.indiatimes.com/city/kanpur/Centre-will-approve-Kanpur-metro-project-soon-Venkaiah-Naidu/articleshow/54693472.cms|title=Centre will approve Kanpur metro project soon: Venkaiah Naidu - Times of India|publisher=|accessdate=24 October 2016}}</ref><ref>{{cite web|url=http://economictimes.indiatimes.com/industry/transportation/railways/centre-approves-plan-for-varanasi-and-kanpur-metro/articleshow/52392309.cms|title=Centre approves plan for Varanasi and Kanpur metro - The Economic Times|publisher=|accessdate=24 October 2016}}</ref>
==दाम==
एह प लागे वाला अनुमानित रकम ₹17,227 करोड़ बाटे।
== संदर्भ ==
{{Reflist}}
rt8bfb8lbso85235ljtrba37qew4a4h
रबड़
0
77522
803367
775290
2026-07-29T16:24:07Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803367
wikitext
text/x-wiki
'''रबड़''' एगो अइसन सामान होला जेकरा से कुछु लिखल चीझु चाहे दागि के मिटकावल जाला। ई बहुत आकर अउर रंग के होखेला। कोनो कोनो पेंसिल के पाछे एगो छोट रबड़ लागल रहेला।
[[File:Office-pink-erasers.jpg|right|thumb|300px|रबड़]]
[[File:Benutzung eines Radiergummies.gif|thumb|रबड़ परजोग कइल]]
सभ से पहिले रबड़ के पेंसिल से लिखला प होखे वाला भूल के मिटकावे ला भइल रहे। बादि मे सेयाहियो के मिटकावे ला रबड़ बनि गइल। सिलेट चाहे ऊज्जर बोर्ड प लिखल मिटकावे ला परजोग होखे अला समानो के रबड़ कहल जाला।
==इतिहास==
[[File:Pencil Eraser.jpg|right|thumb|पेंसिल मे लागल रबड़]]
रबड़ से पहिले कुछु मिटकावे ला मोम के परजोग होखत रहे, आ बलुइया पथल के झिटका सभ के परजोग सेयाही से लिखला प होखे अला भूल मे मिटकावे ला होखत रहे। 1770 मे एगो अंग्रेजी इंजिनियर एडवर्ड नैरने पहिला बेरा रबड़ बनइलन।
2vta84rekbxa8g428se0i6lbfn8azrg
कटर
0
77525
803309
754452
2026-07-29T16:19:52Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803309
wikitext
text/x-wiki
[[File:Sharpener with Pencil.jpg|thumbnail|right|upright=1.2|हाथ से इस्तेमाल होखे वाला कटर]]
[[File:Apsco Vacuhold pencil sharpener 15.ogv|thumb|right|upright=1.2|मोटर से इस्तेमाल होखे वाला कटर]]
'''कटर''' ([[अंग्रेजी]]: पेंसिल शार्पनर चाहे पेंसिल प्वाइंट; [[आयरलैंड]] मे पेरेर भा टॉपर)<ref name="CoughlanHughes2007">{{cite book|author1=Gerry Coughlan|author2=Martin Hughes|title=Irish Language and Culture|url=https://books.google.com/books?id=bhfHeSgnAcQC|year=2007|publisher=Lonely Planet|isbn=978-1-74059-577-3|page=74}}</ref> एगो अऊजार हटे जेकरा पेंसिल के छिल के ओकर नोखि के तेज कइल जाला। एकर संचालन मनई करेला चाहे कोनो मे मोटरो लागल रहेला। कोनो कोनो कटर प ढकना लागल रहेला जेकरा पेंसिल मे छिलकोइया बिगे बजी हटाई दिहल जाला।
==इतिहास==
कटर से पहिले पेंसिल के छुड़ी से तेज कइल जात रहे। कटर बनला के बादि इ काज बहुते सरल हो गइल आ पेंसिल बेसी नीमन से छिलाए लागल।
कटर बनावे के सुरुआत फ्रांस मे भइल, जब एगो फ्रंसिसी पुस्तक एगो सिए बाउचर के कटर कर आविष्कार दिया बिस्तार से लिखलस। उहां के पैंटोग्राफ प काज करत रहीं आ उहाँ के पेंसिल छिले ला एगो निमन अउजार के दरकार रहे।<ref>{{Cite book|url=https://books.google.de/books?id=Vut7Bt4AjmIC&pg=PA290&lpg=PA290&dq=boucher+taille+crayon&source=bl&ots=lsz_4RhR9w&sig=ACfU3U3I6fQPJYCAgMn8uZCalI7ZnxhQ6A&hl=de&sa=X&ved=2ahUKEwj7ltDRyeHgAhWNJVAKHQ-dBW8Q6AEwCHoECAYQAQ#v=onepage&q=boucher%20taille%20crayon&f=false|title=Recueil de la Société polytechnique: ou Recueil industriel, manufacturier, agricole et commercial, de la salubrité publique, ...|last=|first=|date=1822|publisher=Société polytechnique|year=|isbn=|location=|pages=290-295|language=fr}}</ref><ref name="Grahl">{{Cite web|url=http://patent-infos.de/overview/history-pencil-sharpeners-1850.html|title=History of pencil sharpeners and pointers -1850 Lassimone, Cooper/Eckstein, Lahausse|last=Grahl|first=André|website=patent-infos.de|access-date=2019-03-01|archive-url=https://web.archive.org/web/20191113171647/http://patent-infos.de/overview/history-pencil-sharpeners-1850.html|archive-date=13 November 2019|url-status=dead}}</ref> बाउचर के जंत्र बहुते नीमन रहे आ देस हुनकर एकरा ला देस बिदेस मे तारिफो भइल।<ref>{{Cite book|url=https://books.google.de/books?hl=de&id=Vm9QAAAAcAAJ&dq=allgemeine+maschinen+encyclop%C3%A4die&q=boucher#v=snippet&q=boucher&f=false|title=Allgemeine Maschinen-Encyclopädie: A - Beu|last=Hülsse|first=Julius A.|date=1841|publisher=Voss|year=|isbn=|location=|pages=247|language=de}}</ref> बाकिर बाउचर एपे पटेंट ना लिहलन।
बर्नार्ड लेसिमोन कटर के पेटेंट ला 1828 मे अर्जी दिहलन<ref>{{Cite book|url=https://books.google.de/books?id=BkYFAAAAQAAJ&pg=PA383&dq=lassimonne+taille+crayon&hl=de&sa=X&ved=0ahUKEwjx6O6c8eLgAhXQPOwKHSj6BywQ6AEINDAB#v=onepage&q=lassimonne%20taille%20crayon&f=false|title=Description des machines et procédés spécifiés dans les brevets d'invention, publ. par C.P. Molard. [With] Table générale des vingt premiers volumes...|last=|first=|date=1835|publisher=|year=|isbn=|location=|pages=583, 81-83|language=fr}}</ref> एह पेंटेंट प पेरिस के एगो दोकान कटर बेचलस आ बनईलस। 1833 मे इंग्लैंड के कुपर आ एकस्टाइन, स्टीलॉक्सिनन नाम से एगो पेटेंट लेलन, जेमे दुगो रेती नब्बे डिग्री के कोन प लगाई के सिसम के काठ प लगावल रहे।<ref>{{Cite book|url=https://books.google.de/books?id=esA-AAAAcAAJ&pg=PA318&dq=cooper+eckstein+patent+1833&hl=de&sa=X&ved=0ahUKEwjVpffq6eHgAhXJ-yoKHT1uDS4Q6AEIMzAB#v=onepage&q=cooper%20eckstein&f=false|title=Repertory of patent inventions and other discoveries and improvements in arts, manufactures and agriculture|last=|first=|date=1833|publisher=Macintosh|year=|isbn=|location=|pages=318-319|language=en}}</ref><ref>{{Cite book|url=https://books.google.de/books?id=qoqJvkEyCnIC&printsec=frontcover&hl=de#v=onepage&q=185&f=false|title=Mechanic's Magazine, Museum, Register, Journal & Gazette|last=|first=|date=1837|publisher=Knight and Lacey|year=|isbn=|location=|pages=185|language=en}}</ref> ई आजु ले बाँचल सभ से पुरान कटर बा।<ref>{{Cite web|url=https://www.worthpoint.com/worthopedia/antique-early-patent-pencil-sharpener-633650095|title=RARE ANTIQUE EARLY PATENT PENCIL SHARPENER 1830s - COOPER & ECKSTEIN STYLOXYNON|website=Worthpoint|language=en|access-date=2019-03-01|archive-url=https://web.archive.org/web/20190302024601/https://www.worthpoint.com/worthopedia/antique-early-patent-pencil-sharpener-633650095|archive-date=2 March 2019|url-status=dead}}</ref>
1830 आ 1840 के दसक मे पेरिस केह ढेर जाना जइसे, फ्रनकोइस जोसेफ कटर बनावे मे लागल रहें।<ref>{{Cite book|url=https://books.google.de/books?id=Cf7NAAAAMAAJ&pg=PA406&lpg=PA406&dq=lahausse+taille+crayon&source=bl&ots=NwqKUF0Quf&sig=ACfU3U3u4nPI_8chYc224NRbbRsQw-7ikg&hl=de&sa=X&ved=2ahUKEwjXp7C7jeLgAhVRsqQKHQrZAscQ6AEwB3oECAcQAQ#v=onepage&q=lahausse%20taille%20crayon&f=false|title=Bulletin de la Société d'encouragement pour l'industrie nationale|last=|first=|date=1834|publisher=Société d'encouragement pour l'industrie nationale|isbn=|location=|pages=406-407|language=fr}}</ref>
==छबी सभ==
<gallery widths="180">
File:CrankPencilShapener.jpg|Hand-cranked planetary sharpener, with cover in place
File:Apsco Vacuhold pencil sharpener 13.jpg|Hand-cranked planetary sharpener from 1950–60, with cover removed
File:HighEndSharpener.png|Hand-cranked planetary sharpener with spring-driven pencil holder. The small handle behind the crank is the sharpness regulator.
File:SchoolPro Electric Pencil Sharpener.jpg|AC-powered electric pencil sharpener. The mechanism is similar to the planetary sharpener, except there is a motor instead of a crank.
File:PencilSharpenerBatteryOp.jpg|Battery-operated sharpener
</gallery>
==सनर्भ==
qf4z3sk3gbtb2fdj6sb13mhb2wu0ya6
हाथ धोवल
0
77534
803331
754485
2026-07-29T16:21:26Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803331
wikitext
text/x-wiki
{{Infobox medical intervention
| name = हाथ धोवल
| synonym =
| image = OCD handwash.jpg
| caption = साबुन आ पानी से हाथ धोवल
| alt =
| pronounce =
| specialty =
| synonyms =
| ICD10 =
| ICD9 =
| ICD9unlinked =
| CPT =
| MeshID =
| LOINC =
| other_codes =
| MedlinePlus =
| eMedicine =
}}
'''हाथ धोवल''' ([[अंग्रेजी]]: Handwashing) एगो काज हऽ जेमे हाथ के साबुन आ पानी चाहे खाली पानी से साफ कइल जाला, जेकरा से [[वायरस]], बैक्टीरिया चाहे हाथ के मइल हट जाला। हाथ सुखाइलो एकरे हिस्सा मानल जाला एहसे की भींजल हाथ बड़ी हाली दोबेरा लेटा सकेला।<ref name="CDC2020Wash2" /><ref name="Huang2012" />
[[बिस्व स्वास्थ संगठन]] नाहीयो तऽ 20 सेकंड ले हाथ धोवे के सलाह देवेला। हई सभ काम करे के पहिले आ बादि मे हाथ धोवे के चाहीं:<ref>https://www.youtube.com/watch?v=3PmVJQUCm4E</ref>
* कोनो बेमरिया के सेवा करे से पहिले आ बादि मे
* खायका पकावे से पहिले, पकावत बजी आ बादि मे
* खाए से पहिले
* टोइलेट के परयोग करे के बादि
* नाक झड़ला, खोखला, छींकला के बादि
* पसू सभ के छुवला के बादि
* घूरा छुवला के बादि
जदि हाथ मे धुर गरदा नइखे तऽ पानी के बदल सैनिटाइजर के परजोग कइल जा सकेला।<ref>{{cite web |title=Coronavirus Disease 2019 (COVID-19) |url=https://www.cdc.gov/coronavirus/2019-ncov/prevent-getting-sick/disinfecting-your-home.html |website=Centers for Disease Control and Prevention |language=en-us |date=11 February 2020}}</ref><ref name="Centers for Disease Control">{{cite web |title=When and How to Wash Your Hands|author= Centers for Disease Control |url=https://www.cdc.gov/handwashing/when-how-handwashing.html |website=cdc.gov |language=en-us |date=2 April 2020}}</ref> जदि साबुन आ सैनिटाइजर दूनो नइखे तऽ हाथ के राख अउरी पानी से धोवल जा सकेला, बाकिर कोसिस होए के चाहीं जे साबुन चाहे सैनिटाइजर के परजोग कइल जाव।
साबुन से दिन मे बेरि बेरि हाथ धोवला से ढेर बेमारी से बाचल जा सकेला जइसे डाइरिआ आ कलेरा। एकरा से खोखी चाहे [[कोविड-19|कोरोना]] बेमारी से भी बाचल जा सकेला।
==सनर्भ==
qlmnqw7z7r2fb5tk46v3d76cq3oqyic
बियोनसे
0
77567
803359
777022
2026-07-29T16:23:28Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803359
wikitext
text/x-wiki
{{Infobox musical artist
| image = Beyonce - The Formation World Tour, at Wembley Stadium in London, England.jpg
| name = बियॉन्से नोवेल्स
| image_size = 220px
| caption = बियॉन्से नोवेल्स 2019 मे
| background = solo_singer
| birth_name = बियॉन्से गज़ेल नोवेल्स
| alias = साशा फ़िर्स<!--NOTE TO EDITORS: Sasha Fierce is the name of Knowles' alter-ego. This name therefore is an alias per [[Template:Infobox Musical artist#Alias]], please do not add any other nicknames.-->
| birth_date = {{birth date and age|mf=yes|1981|9|4}}<ref name="Born to be a Star">{{cite news |url=http://www.independent.co.uk/news/people/profiles/beyonc-born-to-be-a-star-1890924.html |last=ऐडम्स |first=गाय |title=बियॉन्से: बॉर्न टु बि अ स्टार |work=द इंडिपेंडंट |location=युके |date=फ़रवरी 6, 2010 |accessdate=जनवरी 9, 2011 |archive-url=https://web.archive.org/web/20180617220026/https://www.independent.co.uk/news/people/profiles/beyonc-born-to-be-a-star-1890924.html |archive-date=17 जून 2018 |url-status=live }}</ref>
| origin = होस्टन, टेक्सास, अमेरिका
| instrument = आवाज़
| genre = आराइंडबी, [[पॉप]], सोल, हिप हॉप
| occupation = गायिका, रेकॉर्ड निर्माता, अभिनेत्री, नर्तक, छायाकार, मॉडल, फ़ैशन डिज़ाइनर
| associated_acts = डेस्टिनिज़ चाइल्ड, [[जे-ज़ी]], सोलांजे नॉलेस, कॉने वेस्ट, [[लेडी गागा]]
| years_active = 1997–अबतक
| label = कोलंबिया
| website = {{official URL|www.beyonceonline.com}}
<br />[[चित्र:Beyoncé autograph.svg|100px]]<br />बियॉन्से के हस्ताक्षर
}}
'''बियॉन्से गज़ेल नोवेल्स कार्टर''' (जनम: 4 सितंबर 1981) एगो अमेरिकी गवईया, नर्तकी, अभिनेत्री बाड़ी। संगीत, नृत्य आ फैशन मे हिनकर परभवसाली जोगदान हिनकरा के एगो वैश्विक पहिचान दियवले बाटे। ह्यूस्टन मे जनमल आ पिसैल, बियोनसे लइकाइं मे बहुते गावे आ नाचे के प्रतिजोगिता मे भाग लिहले रही। 1990 के दसक के अंत मे ई परसिद्ध भइली।
डेस्टिनिज़ चाइल्ड के बाद बियोनसे आपन एकल एल्बम ''डेंजरस्ली इन लव'' 2003 मे अनली जेहमे "क्रेज़ी इन लव" आ "बेबी बॉय" गीत ''बिलबोर्ड'' हॉट 100 मे पहीलका स्थान प रहल। ई एल्बम बछर कर सभसे सफ़ल एल्बम रहल आ एकरा से हुनकरा पांच गो ग्रैमी पुरस्कार मिलल। 2005 मे डेस्टिनिज़ चाइल्ड के टुटला के बादि बियोनसे आपन दोसरका एकल एल्बम ''ब'डे'' 2006 मे रिलीज़ कइली जेमे हिट गीत "इर्रिप्लेसेबल" आ "ब्युटिफ़ुल लायर" रहे। हुनकर तिसरका एकल एल्बम ''आय एम... साशा फ़िर्स'' 2008 मे आइल जेकरा मे चार गो व्यापारिक तौर प सफ़ल गाना —"इफ़ आय वर अ बॉय", "सिंगल लेडिज़ (पुट अ रिंग ऑन इट)", "हेलो" आ "स्विट ड्रिम्स" रहे। इ एलबम हुनकरा छव गो ग्रैमी पुरस्कार दियवइलस आ एगो राति मे सभसे बेसी ग्रैमी पुरस्कार जिते वाला पहिलका मेहरारू बनि गइली।<ref>{{cite web|last=सिसारिओ|first=बेन|url=http://www.nytimes.com/2010/02/02/arts/music/02grammys.html|title=ऐट हॉट कॉन्टेस्ट, कूल बियॉन्से इज़ अ डायनामो बट नेवर अ डिवा|work=द न्यु यॉर्क टाइम्स|publisher=द न्यु यॉर्क टाइम्स कंपनी|date=फ़रवरी 1, 2010|accessdate=फ़रवरी 1, 2010|archive-url=https://web.archive.org/web/20100211040834/http://www.nytimes.com/2010/02/02/arts/music/02grammys.html|archive-date=11 फ़रवरी 2010|url-status=live}}</ref> तिन बरस बादि बियोनसे आपन चउथा एकल एल्बम "4" (2011) रिलीज़ कइली जे हूनकरा ''बिलबोर्ड'' 200 के पहिलका पायदान प रहे आला लगातार चउथा अल्बम रहल। हेकरा से ऊ दोसरका मेहरारू कलाकार आ तिसरका कलाकार बनि गइली जेकर चार गो एल्बम चार्ट के पहिलका क्रमांक प रहल।
संगित काज के बादि बियोनसे अभिनय कला मे अइली। उ 2001 मे बनल संगितमय फिलिम ''कार्मेन: अ हिप हॉपेरा'' से पदार्पण कइली आ बादि मे ''ऑस्टिन पॉवर्स इन गोल्डमेम्बर'' (2002) आ ''ड्रिमगर्ल्स'' (2006) खानी बड फ़िल्मन मे काज कइली जेकरा ला हुनकरा के दुगो [[गोल्डन ग्लोब पुरस्कार|गोल्डन ग्लोब पुरस्कारों]] ला नामांकन मिलल। उ 2008 मे बनन ''कैडिलैक रेकॉर्ड्स'' मे अभिनय किया। 2004 मे बियोनसे आ हुनकर माई मिलके आपन पारिवारिक फ़ैशन शृंखला हाउस ऑफ़ डेरिऑन चालू कइल। 2010 मे उ लोग ''[[फ़ोर्ब्स]]'' के बिश्व कर 100 सभसे बेसी बरियार आ प्रभावपुर्ण गायकन के लिस्ट पहिलका स्थान प<ref>{{cite web |url=http://www.forbes.com/2010/06/22/beyonce-lady-gaga-business-entertainment-celeb-100-10-musicians_slide.html |title=सेलेब्रिटी 100: म्युज़िशियन्स |work=फ़ोर्ब्स |date=जुन 29, 2010 |accessdate=जुन 29, 2010 |archive-url=https://www.webcitation.org/65iBE8cnq?url=http://www.forbes.com/2010/06/22/beyonce-lady-gaga-business-entertainment-celeb-100-10-musicians_slide.html |archive-date=25 फ़रवरी 2012 |url-status=live }}</ref> आ होकर बिश्व कर 100 सभसे बेसी बरियार आ प्रभावपुर्ण सिलेब्रिटी के लिस्ट मे दुसरका स्थान प रहली।<ref>{{cite web |author=डोरोथी पोमेरांट्ज़, लेसी रोज़ |url=http://www.forbes.com/2010/06/22/oprah-winfrey-lady-gaga-twilight-business-entertainment-celeb-100-10-intro.html |title=द वर्ल्ड्स मोस्ट पावरफ़ुल सेलेब्रिटिज़ |work=फ़ोर्ब्स |date=जुन 28, 2010 |accessdate=जुन 29, 2010 |archive-url=https://web.archive.org/web/20120113145629/http://www.forbes.com/2010/06/22/oprah-winfrey-lady-gaga-twilight-business-entertainment-celeb-100-10-intro.html |archive-date=13 जनवरी 2012 |url-status=live }}</ref>
बियोनसे के काज हुनकरा कए गो पुरस्कार आ मान दियवले बा जेकरा मे 16 गो ग्रैमी पुरस्कार, 11 गो एमटीवी वीडियो म्युज़ीक पुरस्कार, एगो बिलबोर्ड मिलेनियम पुरस्कार आ हॉलिवुड वॉक ऑफ़ फ़ेम मे डेस्टिनिज़ चाइल्ड संघे एक सितारा बा। 2009 मे ''बिलबोर्ड'' हुनकरा 2000 के दशक कर उच्च रेडियो कलाकार का मन देलस।<ref name="Female artist of the decade">{{cite web|url=http://www.billboard.com/charts-decade-end/hot-100-songs?year=2009&begin=41&order=position#/charts-decade-end/radio-songs-artists?year=2009|title=टॉप 100 म्युज़िक हिट्स, टॉप 100 म्युज़िक चार्ट्स, टॉप 100 सांग्स ऐंड द हॉट 100|work=बिलबोर्ड|date=दिसंबर 31, 2009|accessdate=जुन 6, 2010|archiveurl=https://archive.ph/20120720052938/http://www.billboard.com/charts-decade-end/hot-100-songs?year=2009&begin=41&order=position%23/charts-decade-end/radio-songs-artists?year=2009#/charts-decade-end/radio-songs-artists?year=2009|archivedate=2012-07-20|url-status=live}}</ref> मई 2010 ले बियोनसे [[अमेरिका]] मे 1.2 करोड़ एल्बम की बेचले रही। जनवरी 2012 ले हुनकर 7.5 करोड रेकॉर्ड बिश्वभर मे बेचाइल रहे।<ref name="Newstateman">{{cite web |url=http://www.newstatesman.com/global-issues/2009/09/world-fashion-gay-india-church |title=द 50 पिपल हू मैटर टुडे: 41–50 |work=न्यू स्टेट्समैन |location=युके |date=सितंबर 24, 2009 |accessdate=फ़रवरी 7, 2011 |archive-url=https://web.archive.org/web/20090928040553/http://www.newstatesman.com/global-issues/2009/09/world-fashion-gay-india-church |archive-date=28 सितंबर 2009 |url-status=live }}</ref>
==जिनगी आ कैरियर==
===सुरुआती जिनगी आ कैरियर===
बियॉन्से गज़ेल नोवेल्स, के जनम 4 सितंबर 1981 के [[ह्यूस्टन]], [[टेक्सास]] मे [[सेलेस्टिन "टीना" नोवेल्स]] (née Beyonce), जे हेयरड्रेसर आ सैलून के मालिक रही, आ [[मैथ्यू नोवेल्स]], जे [[जेरोक्स]] के बिक्री प्रबंधक रहन, के घरे भइल; टीना [[लुइसियाना क्रियोल]] हई, आ मैथ्यू [[अफिरकी अमेरिकी]] हवन। बियॉन्से के छोट बहिन [[सोलेंज नोवेल्स|सोलेंज नोवेल्सो]] एगो गवइआ हई आ [[डेस्टिनीज चाइल्ड]] ला बैकअप डांसर रहल बाड़ी। सोलेंज आ बियॉन्से पहिली बहिन बाड़ी जिनहन के नंबर वन एल्बम रहल।
बियॉन्से के नाना-नानी, लुमास बियॉन्से आ एग्नेज डेरेओन (ओडिलिया ब्रौसार्ड आ यूजीन डिरूएन के बेटी) फ्रेंच भासी लुइसियाना क्रियोल रहल लोग, जिनहन के जड़ [[न्यू आइबेरिया]] मे रहल। बियॉन्से के [[क्रियोल लोग|क्रियोल]] मानल जाला, जवन की इनकर अजो-आजी लोग रहल। अपना महतारी के देने से, बियॉन्से फ्रांस के दक्खिन-पच्छिम के कय गो फ्रांसीसी कुलीन लोग के बंसज बाड़ी, जेकरा मे 9वीं सदी से ले के विस्कॉन्ट डी बेअर्न के परिवार आ विस्कॉन्ट डी बेलजुंस के परिवारो बा। ई [[जीन-विंसेंट डी अब्बाडी डी सेंट-कास्टिनो|जीन-विंसेंट डी अब्बाडी डी सेंट-कास्टिन]] के बनज हई, जे एगो फ्रांसीसी धनी बेकत आ सैन्य नेता रहन जे एकेडिया मे अंगरेज सभ के खिलाफ मूल निवासी अबेनाकी के संगे लड़ाई लड़ले रहन, आ एकेडियन नेता जोसेफ ब्रौसार्ड ओ के। इनकर चउथा परदादी मैरी-फ्रांसोइस त्राहन के जनम 1774 मे [[फ्रांस]] के बेले आइल पऽ बसल बैंगोर मे भइल। त्राहन एकेडियन लोग के बेटी रही जे ब्रिटिस देस निकाला के बाद बेले आइ के सरन लेले रहली। एस्टेट्स ऑफ ब्रिटनी बेले आइल के जमीन सभ 78 गो एकेडियन परिवार आ पहिले से बसल निवासी लोग के बीचे बखरा लगा दिहले रहे। त्राहन परिवार दस बरिस से ढेर समय ले बेले आइल पऽ रहल आ एकरे बाद ऊ लोग लुइसियाना मे आप्रवास कइल, जहाँ इनके बियाह एगो ब्रौसार्ड के वंशज से भइल। बियॉन्से उनके वंश पर शोध कइली आ पता चलल कि ऊ एगो गुलाम मालिक के वंशज हवे जे उनके गुलाम से बियाह कइले रहल। इनकर महतारीओ दूर के यहूदी, स्पेनिश, चीनी आ इंडोनेशियाई मूल के हई।
बियॉन्से के कैथोलिक रूप से पोसल गइल बा आ ह्यूस्टन के सेंट मैरी मोंटेसरी स्कूल से इनकर पढ़ाई भइल बा, जहाँ ऊ नाच के कक्षा मे नाम लिखववली। इनकर गवइ के खोज तब भइल जब नाच सिखावे वाली डार्लेट जॉनसन एगो गीत गावे लगली आ ऊ एकरा के खतम कइली, उहो ऊपर के सुर लगा के। सात साल के उमिर मे स्कूल टैलेंट शो जीतला के बादो बियॉन्से के संगीत आ परफार्मेंस मे रुचि जारी रहल, शो मे जॉन लेनन के "इमैजिन" गा के बियॉन्से 15/16 बरिस के लइकन के हरा दिहली। 1990 के दसक के अंत मे, बियॉन्से ह्यूस्टन के एगो संगीत स्कूल पार्कर एलिमेंटरी स्कूल मे नाम लिखववली जहाँ ऊ स्कूल के गवइअन संगे गावत रही। एकरे अलावा ऊ हाई स्कूल फॉर द परफॉर्मिंग एंड विजुअल आर्ट्स मे पढ़ाई कइली आ बाद मे एलिफ एल्सिक हाई स्कूलो मे पढ़ाई कइली। बियॉन्से सेंट जॉन यूनाइटेड मेथोडिस्ट चर्च मे दू साल ले एकलवादक के रूप मे गायन दल के सदस्यो रही।
जबे बियॉन्से आठ बरिस के रहली तबे उनकर भेंट लाटाविया रॉबर्सन से ऑल-गर्ल एंटरटेनमेंट ग्रुप के एगो ऑडिशन मे भइल। एह लोग के तीन गो अउरी लइकिन साथे गर्ल्स टाइम नाँव के एगो ग्रुप मे राखल गइल, आ ई लोग ह्यूस्टन के टैलेंट शो सर्किट पर रैप आ डांस कइलस। समूह के देखला के बाद आर एंड बी निर्माता अर्ने फ्रेगर ई लोग के अपना नॉर्दर्न कैलिफोर्निया स्टूडियो मे ले अइले आ स्टार सर्च मे रखलें जे ओह घड़ी के नेशनल टीवी पऽ सभसे बड़ टैलेंट शो रहे। गर्ल्स टाइम जिते ना पइलस आ बाद मे बियॉन्से कहली कि ऊ लोग जवन गीत पेस कइलस ऊ नीमन ना रहे 1995 मे बियॉन्से के बाबू एह समूह के प्रबंधन करे ला अपना नौकरी छोड़ि दिहलें। एकरा से बियॉन्से के परिवार के आमदनी आधा हो गइल अउरी उनकर माइ-बाबू के आपन घर अउरी गाड़ी बेच के अलग-अलग अपार्टमेंट मे रहे के पड़ल। मैथ्यू मूल लाइन-अप के चार कर दिहलें आ ई समूह अन्य स्थापित आर एंड बी गर्ल ग्रुप सभ खातिर एगो ओपनिंग एक्ट के रूप में परफार्म करत रहल। ई लइकियन के रिकार्ड लेबल सभ से पहिले ऑडिशन दिहल गइल आ अंत मे इलेक्ट्रा रिकार्ड्स मे साइन कइल गइल, कुछ समय खातिर अटलांटा रिकार्ड्स में आ गइलें आ अपना पहिली रिकार्डिंग पर काम कइलें, बाकी कंपनी द्वारा इनहन के काट दिहल गइल। एहसे परिवार पऽ अउरी तनाव पैदा हो गइल अउरी बियॉन्से के माइ-बाबू अलग हो गइल लोग। 5 अक्टूबर 1995 के ड्वेन विगिन्स के ग्रास रूट्स एंटरटेनमेंट एह समूह के साइन कइलस। 1996 मे ई लइकी लोग सोनी म्यूजिक संगे समझौता के तहत आपन पहिला एल्बम रिकार्ड कइल शुरू कइलस, नोल्स परिवार फेर से एक हो गइल, एह समूह के कोलंबिया रिकार्ड्स के साथ अनुबंध मिलल।
===1997-2002: डेस्टिनिज चाइल्ड===
{{Main|डेस्टिनिज चाइल्ड}}
[[File:Destiny's Child Tour.jpg|thumb|left|डेस्टिनिज चाइल्ड के अंतिम लाइन-अप मे बियॉन्से (बीचे), 2005 के डेस्टिनी फुलफिल... एंड लविन इट संगीत कार्यक्रम मे परफार्म करत]]
==सनर्भ==
{{Singer-stub}}
6h7kk13jc25c9p782kv6k58tdadfbt4
क्रेयोन शिनचैन
0
78722
803351
722533
2026-07-29T16:22:50Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803351
wikitext
text/x-wiki
'''क्रेयोन शिनचैन''' ([[जापानी भाषा|जापानी]]: クレヨンしんちゃん, Hepburn: Kureyon Shin-chan), जेकरा '''शिनचैन''' नांव से जानल जाला, योशितो उसुई के लिखल एगो जापानी मांगा हऽ। एमे शिन्नोसुके "शिनचैन" नोहारा नांव के एगो पाँच बरिस के लइका, ओकर पिता,माई, बहिन आ ओकर संघतिया के कहानी हटे। एके जापान के कासुकाबे नगर के देखावल गइल बा।
शिनचैन पहिलका बेरा 1990 मे मांगा एक्शन नांव के एगो साप्ताहिक मैगज़ीन मे छपल रहे। योशितो उसुई के मुअला के चलते असल मांगा के 11 सितंबर 2009 के बन करे के पड़ल। नयका मांगा 2010 मे न्यू क्रेयोन शिनचैन नांव से चालू भइल (新クレヨンしんちゃん, Shin Kureyon Shin-chan)। एकर एनिमे बनल 1992 मे चालू भइल जे आज ले चलऽता आ तीस से बेसी भासा मे आ चुकल बा। ई 45 देसन मे देखावल जा चुकल बा, आज ले 1007 गो एपिसोड आ 26 गो फिलिम बनल बा। अभी ले 27.5 करोड़ गो मांगा बेचाइल बा।
==परिचय==
[[File:Tobu Crayon Shin-Chan train 2016-12-18.jpg|thumb|जापान के एगो रेलगाड़ी जेपे क्रेयोन शिनचैन बनल बा]]
एकर असल मे ढेर सभ मजाक चाहे चुटकुला अइसन बा जेमे सही भासा के परजोग नइखे कइल गइल। एहिसे अइसन लोग जे जापानी ना जाने उ लोग एकर मजाक के बुझ ना पावेला। कोनो कोनो के तऽ दोसर भासा मे अनुवादो ना कइल जा सके। कुछो निश्चित वाक्य चाहे कहाउत लगभग हरमेशा कुछो काम के संगे होला; एमे से कए गो वाक्य सभ के निश्चित जबाब होला। एकरे चलते अइसन देखे के मिलेला जे शिनचैन कए बेरा आपन माई बाबू के लगे ओह घड़ी के हिसाब से गलती वाक्य के परजोग करेला; जइसे की; जब शिनचैन घरे आवेला तऽ "Welcome Back" (भोजपुरी: स्वागत बा; जापानी: おかえりなさい; "okaeri nasai") बोलेला; बलुक ओकरा "I am home" (भोजपुरी: हम घरे आ गइनी; जापानी: "ただいま" "Tadaima") बोले के चाहीं। अनुबाद करे मे एगो अउरी परेसानी ओनोमेटोपोइक (अइसन शब्द जेकर उच्चारन ओकर माने बतावेला; जइसे घड़ी के आवाज खाती टिक टिक शब्द) जापानी शब्दन के उपजोग से जनमेला।
कुछू अउर हसे वाला बात जे श्रृंखला मे दोहरावल जाला, ऊ सभ अधिक सार्वभौमिक (सभ जगहा एके लेखा) गुन के होला, जइसे अइसन काम सभ जेकरा से लोग हसे (जैसे चॉपस्टिक से बरफ खाइल) चाहे, लइका होला के बादो, सेयान लेखा बोलल। बाकिर तबो, बहुते सभ चिझ के बुझ के सराहे ला जापानी संस्कृति आ/चाहे भासा के समझ होखे के चाहीं; जइसे की, शिनचैन के "मिस्टर एलीफैंट", भले इ बुझा जाला बाकिर, एकर संबंध एगो लइकन सभ के जापानी गीत "ज़ो-सान" (ぞ さ さ ん) से बा। शिनचैन हमेसा अपना से बड़ सुन्नर लइकी सभ के देख के पगला जाला, आ एकरा मे बेसी हसि तब आवेला जब शिनचैन छोट लइका लेखा ई लोग के लोभावे के कोसिस करेला, जइसे कि ऊ लोग से पूछ के (खराब ढंग से) "का तोहरा शिमला मिर्च पसन बा?" (ー き マ ー 好 ー?)। शिनचैन अपना से बड़ लोग से नीमन से ना बतियावे, जइसे की, खिसियाह लोग से पूछ के की "पुलिस लगे तू कतना बेरा गइल बाड़ऽ?" चाहे बुढ़ लोग से पूछ के की "तू कय बरिस के बाड़ऽ?"।
[[श्रेणी:जापानी मांगा]]
8zl0c50ddjrykpdz2rdqt09d2yvy0ob
एलिज़ाबेथ ऑलसेन
0
78730
803302
752761
2026-07-29T16:07:00Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803302
wikitext
text/x-wiki
{{Infobox person
| name = एलिज़ाबेथ ऑलसेन
| image = File:Elizabeth Olsen1 (cropped).jpg
| caption = एलिज़ाबेथ ऑलसेन
| birth_name = एलिज़ाबेथ चेस ऑलसेन
| birth_date = {{Birth date and age|1989|2|16}}
| birth_place = [[लॉस एंजिलिस]], अमेरिका
| alias = लिज्जी ऑलसेन
| alma_mater = न्यू यॉर्क विश्वविद्यालय
| occupation = अभिनेत्री
| years_active = 1993–अबहिन ले
| partner =
| relatives =
}}
'''चार्ल्स एलिज़ाबेथ ऑलसेन''' (जनम: फरवरी 16, 1989) एगो अमेरिकी अभिनेत्री हई। 2011 मे आइल इनकर फिलिम "मार्था मर्सी मे मारलिन" से इनकरा नाम मिलल, आ एकरा ला इनकरा के कए गो पुरस्कार मिलल। बादि मे ई साइलेंट हाउस (2011), लीबरल आर्ट्स (2012), ओल्डब्वॉय (2013), गॉडजिला (2014), आई सॉ दी लाइट (2015), विंड रिवर (2017) आ इंग्रिड गोज वेस्ट (2017) फिल्म सभ में काज कइली। टेलीविज़न प, ई ''फेसबुक वॉच'' के ड्रामा सीरीज़ '''सॉरी फॉर योर लॉस''' (2012-2019) मे अभिनय किया कइली, जेकरा ला इनकरा क्रिटिक्स चॉइस टेलीविज़न अवार्ड ला नामांकित कइल गइल।
[[श्रेणी:1989 में जनम]]
[[श्रेणी:अमेरिकी फिलिम ऐक्ट्रेस]]
[[श्रेणी:जियत लोग]]
{{Actor-stub}}
bqxzc31h042jswm5zm6ah6towaevx9u
गरेना फ्री फायर
0
80250
803369
799254
2026-07-29T16:24:18Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803369
wikitext
text/x-wiki
{{Infobox video game
| title = गरेना फ्री फायर
| image =
| developer = 111डोट स्टुडियो
| publisher = गरेना इंटरनेशनल प्राइवेट लिमिटिड
| director =
| producer =
| designer =
| engine = युनिटी
| platform = एन्ड्रॉयड<br />माइक्रोसॉफ्ट विंडोज़<br />आइओएस<br />एक्सबॉरक्स वन<br />प्लेस्टेशन <br />
| release = 4 दिसंबर, 2017
| genre = बैटल रॉयल|बैटल गेम
| modes = मल्टीप्लेयर
}}
'''गरेना फ्री फायर''', जेकरा के '''फ्री फायर (FF)''' भी कहल जाला, एगो बैटल रॉयल गेम हवे जे गरेना द्वारा एंड्रॉयड आ आईओएस खातिर बिकसित आ प्रकाशित कइल गइल। ई 2019 में वैश्विक स्तर पर सभसे ढेर डाउनलोड होखे वाला मोबाइल गेम बन गइल। 2021 ले, फ्री फायर 15 करोड़ दैनिक सक्रिय प्रयोगकर्ता लोग के पार क चुकल रहे।<ref>{{Cite web |last=Obedkov |first=Evgeny |date=2021-08-17 |title=Mobile battle royale Free Fire surpasses 150 million peak daily active players |url=https://gameworldobserver.com/2021/08/17/garena-free-fire-surpasses-150-million-peak-daily-active-players |access-date=2022-10-14 |website=Game World Observer |language=en-US}}</ref><ref>{{Cite web |title=Garena Free Fire Overtakes PUBG Mobile as the Top Grossing Mobile Battle Royale Game in the U.S. |url=https://sensortower.com/blog/state-of-mobile-battle-royale-q1-2021 |access-date=2022-10-14 |website=sensortower.com}}</ref> फ्री फायर मैक्स, फ्री फायर के ग्राफिक रूप से बढ़ावल संस्करण, 28 सितंबर 2021 के वैश्विक स्तर पर रिलीज भइल।<ref>{{Cite news|date=28 September 2021|title=Garena Free Fire Max is now available: How to download, supported platforms, features and more|work=[[The Times of India]]|url=https://timesofindia.indiatimes.com/gadgets-news/garena-free-fire-max-is-now-available-how-to-download-supported-platforms-features-and-more/articleshow/86587488.cms|access-date=2 October 2021}}</ref><ref>{{Cite web |date=2021-09-22 |title=Free Fire MAX: Release date, pre-registration details, exclusive features |website=One Esports |url=https://www.oneesports.gg/free-fire/free-fire-max-game-release-date/ |access-date=2022-10-17 |language=en-US}}</ref>
== मालिक ==
फॉरेस्ट ली फ्री फायर के निर्माता गैरेना के संस्थापक हवें आ ई गेम बनावल उनकर बिचार रहे। एकरे नतीजा बा कि फॉरेस्ट ली के एह खेल के मालिकाना हक बा। फॉरेस्ट ली वर्तमान में अपना कंपनी गैरेना के [[अध्यक्ष]] आ सीईओ के रूप में काम करे लें।
== खेल ==
फ्री फायर थर्ड पर्सन परिप्रेक्ष्य में खेलल जाला। जइसे कि अउरी शूटिंग गेम सभ में, खिलाड़ी जॉयस्टिक के इस्तेमाल से किरदार के हिला सके ला आ फायर बटन से आइटम सभ के गोली मार सके ला आ फेंक सके ला। गेम में मौजूद पात्र कूद सकेलें, रेंगत हो सकेलें आ लेट सकेलें. जब लड़ाई के मैदान में होखे तब खिलाड़ी लोग "ग्लू वॉल" नाँव के ग्रेनेड के इस्तेमाल कवर के रूप में क सके ला ताकि नुकसान ना होखे।
=== गेम के मोड ===
'''बैटल रोयाल मोड (Battle Royale Mode)''' में अधिकतम 52 खिलाड़ी बिना हथियार के एगो बेतरतीब द्वीप पर उतर सकेलें। सभके एक दूसरा के खतम क के जिए के पड़ेला, जवना में हथियार अउरी सामान के इस्तेमाल कइल जाला जवन कि इमारत में मिलेला। खिलाड़ी लोग के एह मोड के सोलो, या 2 या 4 खिलाड़ी के टीम में खेले के विकल्प बा। अगर खिलाड़ी एह मोड के रैंकिंग में खेलेलें त एकर असर ओह लोग के रैंक पर पड़ी. जबकि क्लासिक मोड सिर्फ मस्ती अउरी अभ्यास खाती बा। फ्री फायर में छह गो बैटल रोयाल मैप बाड़ें ऊ बाड़ें बरमुडा (Bermuda), बरमूडा रिमास्टरड(Bermuda Remastered), कलाहारी (Kalahari), परगेटारी (Purgatory), अल्पाइन(Alpine), आ नेक्सटेरा(NeXTerra)<ref>{{Cite web |title=The Next Garena Free Fire Map Is Called NeXTerra, Launching August 20 |url=https://www.gamespot.com/articles/the-next-garena-free-fire-map-is-called-nexterra-launching-august-20/1100-6506565/ |access-date=2022-11-14 |website=GameSpot |language=en-US}}</ref>.
'''क्लैश स्क्वाड (Clash Squad)''' 4 बनाम 4 मोड के बा। एह मोड में खिलाड़ी लोग गेम के भीतर के पइसा के इस्तेमाल तइयारी के समय में कौनों दुकान से हथियार आ अउरी सामान खरीदे खातिर करे ला, आ फिर बिरोधी टीम से लड़ाई करे ला। बेस्ट ऑफ 7 मोड के रूप में खेल चार से सात राउंड तक चलेला। एगो दौर तब जीतल जाला जब एक टीम के सभ 4 गो खिलाड़ी बाहर हो जालें, या त दुसरा टीम द्वारा या फिर पर्यावरण के नुकसान के कारण। 2022 के हिसाब से ई गेम बैटल रोयाल मोड के समान नक्शा सभ के इस्तेमाल करे ला। क्लैश स्क्वाड के रैंकिंग आ क्लासिक दुनु के रूप में खेलल जा सकेला.<ref>{{Cite web |last=Thakkar |first=Nishant |title=What is the Clash Squad in Free Fire? |url=https://www.sportskeeda.com/free-fire/what-clash-squad-free-fire |access-date=2022-10-17 |website=www.sportskeeda.com |language=en-us}}</ref>
'''लोन वुल्फ (Lone Wolf)''' या त क्लैश स्क्वाड मोड के 1 बनाम 1 या फिर "ड्यूओ" (2 बनाम 2) संस्करण हवे जेह में अउरी जटिल नियम बाड़ें। हर पक्ष के अगिला 2 राउंड में इस्तेमाल होखे वाला उपकरण चुने के मौका मिली। राउंड बेस्ट ऑफ 5 खेलल जाला। अगर दुनु टीम 4-4 राउंड जीतले बा त आखिरी दौर के शुरुआत दुनु टीम के अपना पसंद के हथियार चुने के आजादी से होखी. सभ खिलाड़ी दौर के शुरुआत से ही डिफ़ॉल्ट रूप से बनियान अउरी हेलमेट जईसन बेसिक आइटम हासिल करेले। ई मोड अपना खुद के नक्शा में होला जेकरा के "लोहा गुंबद(Iron Dome)" कहल जाला।<ref>{{Cite web |date=2021-11-28 |title=Garena Free Fire: Lone Wolf Mode coming! A new weapon strategy to go live too |url=https://tech.hindustantimes.com/gaming/news/garena-free-fire-lone-wolf-mode-coming-a-new-weapon-strategy-to-go-live-too-71638080044854.html |access-date=2022-10-13 |website=HT Tech |language=en}}</ref>
'''क्राफ्टलैंड(Craftland)''' खिलाड़ियन के गेम में बिल्डर के इस्तेमाल से आपन फ्री फायर मैप बनावे देला। क्राफ्टलैंड फीचर के मदद से खिलाड़ी अपना बनावल नक्शा के बना सकतारे, साझा क सकतारे अउरी ओकरा संगे खेल सकतारे। ई एडवांस कस्टमाइजेशन खातिर स्क्रिप्ट एडिटिंग के सपोर्ट करे ला जेह से रचनाकार लोग के अपना बिचार सभ के साथ काम करे के ढेर आजादी मिले ला। खिलाड़ी लोग नक्शा में बिल्डिंग, गेमप्ले आइटम, आ सजावट के तरीका अपना तरीका से जोड़ सके ला जबले कि जगह के सीमा अधिकतम स्तर ना हो जाव। दू गो नक्शा बा जवना के इस्तेमाल खिलाड़ी आपन डिजाइन बनावे में कर सकेलें। <ref>{{Cite web |last=Biswas |first=Apurba |date=2021-09-20 |title=Garena Free Fire Max Craftland: Create Your Own Battlefield & Invite Your Friends to Play, More Details - Inside Sport India |url=https://www.insidesport.in/garena-free-fire-max-craftland-create-your-own-battlefield-invite-your-friends-to-play-more-details/ |access-date=2022-10-14 |website=www.insidesport.in |language=en-US |archive-date=2022-10-14 |archive-url=https://web.archive.org/web/20221014141643/https://www.insidesport.in/garena-free-fire-max-craftland-create-your-own-battlefield-invite-your-friends-to-play-more-details/ |url-status=dead }}</ref><ref>{{Cite web |last=Forst |first=Sarah |date=2022-05-23 |title=Free Fire's OB34 Craftland Stuns Gamers Like Never Before! |url=https://techwafer.com/free-fires-ob34-craftland-stuns-gamers-like-never-before/ |access-date=2022-10-14 |website=TechWafer |language=en-US |archive-date=2022-10-14 |archive-url=https://web.archive.org/web/20221014141643/https://techwafer.com/free-fires-ob34-craftland-stuns-gamers-like-never-before/ |url-status=dead }}</ref>
=== कैरेक्टर सिस्टम ===
फ्री फायर में 50+ कैरेक्टर के कैरेक्टर सिस्टम बा। हर पात्र के एगो अलगे कौशल होला जवन सक्रिय भा निष्क्रिय हो सकेला. सक्रिय कौशल के गेम में बटन से मैन्युअल रूप से ट्रिगर कइल जा सके ला आ निष्क्रिय कौशल के स्वचालित रूप से ट्रिगर कइल जाला। हर पात्र पात्र के कौशल आ अन्य पात्रन से 3 गो कौशल समेत 4 गो कौशल से लैस कर सकेला (एक गो सक्रिय कौशल आ तीन गो निष्क्रिय कौशल के एकही पात्र से लैस कइल जा सकेला)। फ्री फायर के पात्रन के फ्री फायर यूनिवर्स में अनोखा कहानी बा। एह में से कुछ किरदार वास्तविक जीवन के सेलिब्रिटी लोग से प्रेरित रहलें जिनहन के साथे फ्री फायर के सहयोग रहल जइसे कि क्रिस्टियानो रोनाल्डो(Cristiano Ronaldo) के साथ सहयोग से क्रोनो(Chrono)<ref>{{Cite web |last=Rossel |first=John Dave |title=Garena Free Fire Officially Announced Collaboration with Cristiano Ronaldo in Operation Chrono Update |url=https://afkgaming.com/mobileesports/news/5723-garena-free-fire-officially-announced-collaboration-with-cristiano-ronaldo-in-operation-chrono-update |access-date=2022-10-27 |website=AFK Gaming |language=en}}</ref>, डीवीएलएम(DVLM) के साथ सहयोग से दिमित्री(Dimitri) एंड थिवा(Thiva)<ref>{{Cite web |first=Rahul |last=Majumdar |date=2021-08-20 |title=Garena Free Fire 4th Anniversary Missions, Rewards and Celebration Song ‘Reunion’ Revealed |url=https://in.ign.com/garena-free-fire-winterlands/164998/news/garena-free-fire-4th-anniversary-missions-rewards-song-reunion-revealed |access-date=2022-10-27 |website=IGN India |language=en-in}}</ref>, एनिटटा(ANITTA) के सहयोग से ए-पट्रोआ(A-Patroa)<ref>{{Cite web |last=Negreira |first=Camila |date=2022-07-01 |title=Anitta’s Free Fire character will be available on July 2 for free |url=https://dotesports.com/mobile/news/anittas-free-fire-character-available-on-july-2-for-free |access-date=2022-10-27 |website=Dot Esports |language=en-US |archive-date=2022-10-27 |archive-url=https://web.archive.org/web/20221027023929/https://dotesports.com/mobile/news/anittas-free-fire-character-available-on-july-2-for-free |url-status=dead }}</ref> , डीजेआलोक(DJ Alok) के सहयोग से एलोक(ALOK) और जस्टिन बेबर(Justin Beiber) के साथे सहयोग से आ जेबेइब्स(JBeibs)<ref>{{Cite web |title=Justin Bieber Is Performing And Debuting A New Song In Garena Free Fire |url=https://www.gamespot.com/articles/justin-bieber-is-performing-and-debuting-a-new-song-in-garena-free-fire/1100-6505148/ |access-date=2022-11-11 |website=GameSpot |language=en-US}}</ref> के साथे।
=== इन-गेम ईवेंट्स ===
फ्री फायर एगो वीडियो गेम हवे जेह में बिबिध किसिम के इन-गेम इवेंट सभ के सामिल कइल जाला। ई इवेंट या त पेड हो सके लें या फिर फ्री आ खिलाड़ियन के गेम में इनाम आ बोनस कमाए के मौका देला। पेड इवेंट, जइसे कि एलिट पास (अब 55 सीजन<ref>{{Cite web|url=https://gamesroid.com/free-fire-all-elite-pass-bundle-list|title=Free Fire Elite Passes: Overview Of All 55 Seasons|date=1 April 2022|website=GamesRoid.com|language=en-GB|access-date=30 September 2022}}</ref> के बाद बैटल पास के जगह ले लिहल गइल) आ डायमंड रोयाल, खिलाड़ी लोग के एक्सक्लूसिव कॉस्मेटिक्स के अनलॉक करे के इजाजत देला या वर्चुअल करंसी के इस्तेमाल से बिसेस आइटम भा बोनस खरीद सके ला। दूसर ओर मुफ्त इवेंट सभ में आमतौर पर सभ खिलाड़ियन के उपलब्धता होला आ एह में भाग लेवे खातिर कौनों भुगतान के जरूरत ना पड़े ला। एह इवेंट सभ में गेम में बिसेस चुनौती भा सीमित समय के इवेंट सामिल हो सके लें जेह में खिलाड़ियन के बिसेस इनाम आ बोनस कमाए के मौका मिले ला। एह आयोजनन के विषय अलग अलग मौका पर अलग अलग हो सकेला.
== परिसर ==
फ्री फायर के आधार के बिस्तार से एनिमेटेड शॉर्ट्स आ अउरी सामग्री सभ में बतावल गइल बा जे गैरेना द्वारा एह गेम के प्रचार खातिर प्रकाशित कइल गइल बा। फ्री फायर यूनिवर्स(Free Fire Universe) आधिकारिक ब्रह्मांड हवे जहाँ फ्री फायर गेम, कॉमिक्स आ कहानी (फ्री फायर टेल्स) होला। हर फ्री फायर किरदार के कहानी आ पारिवारिक पृष्ठभूमि अलग होला आ फ्री फायर यूनिवर्स में कहानी से पात्र एक दुसरा से जुड़ल बाड़ें। ब्रह्मांड में तीन गो शहर बा जवना के नाम बा LA LUNA, GRIZA, आ NEW DAWN जहाँ हर पात्र रहेला. फ्री फायर के पात्रन के दू गो अंश में बाँटल गइल बा HORIZON आ MAMBAS, HORIZON शहर के शासक अंश हवे जहाँ अमीर आ ताकतवर लोग बा जबकि MAMBAS HORIZON के खिलाफ एगो मुक्ति समूह हवे। साल 2022 में फ्री फायर फ्री फायर यूनिवर्स के पहिली छोट फिलिम "हाउ टू स्टार्ट ए फायर(How to Start a Fire)" रिलीज कइलस जेह में फ्री फायर के एगो किरदार हयातो यागामी(Hayato Yagami) के देखावल गइल।
== अगवानी ==
एह ग्राफिक्स के "मिड आ लो स्पेसिफिकेशन वाला फोन खातिर फायदा" बतावल गइल, बाकी एगो समीक्षक के कहनाम बा कि "अगर बढ़िया ग्राफिक्स वाला गेम आपके चीज बा त हमनी के सलाह ना देत बानी जा कि रउआ फ्री फायर बैटलग्राउंड खेलीं। लेकिन अगर रउआ पसंद बा।" बैटल रॉयल गेम आ अपना दोस्तन के साथे मस्ती करे के चाहत बानी त रउरा जरूर खेले के चाहीं."
टेक्टुडो (Techtudo) के ताईस कारवाल्हो (Tais Carvalho) टिप्पणी कइलें कि फ्री फायर "परफार्मेंस के प्राथमिकता देला, जेकरा चलते ई कवनो भी प्रकार के डिवाइस खातिर एगो बढ़िया विकल्प बा। गेमप्ले अलगा से खड़ा बा आ मनोरंजन खातिर पर्याप्त सामग्री बा आ फायदेमंद लड़ाई पेश करे खातिर।" किरदार आ कौशल के प्रगति का बारे में ऊ कहली कि ई एगो चमकदार ऐड-ऑन ह। <ref>{{Cite web|last=Carvalho|first=Taís|date=5 March 2018|title=Free Fire ou Rules of Survival: veja qual o melhor Battle Royale|url=https://www.techtudo.com.br/noticias/2018/03/free-fire-ou-rules-of-survival-veja-qual-o-melhor-battle-royale.ghtml|access-date=7 January 2021|website=TechTudo|language=pt-br}}</ref>
गूगल प्ले के सालाना "बेस्ट ऐप ऑफ द ईयर" के लिस्ट में फ्री फायर 2019 के "बेस्ट पॉपुलर वोट गेम" श्रेणी में जीतल, ब्राजील आ थाईलैंड में सभसे ढेर सार्वजनिक रूप से वोट कइल गइल।
गरेना फ्री फायर सभसे लोकप्रिय बैटल रॉयल मोबाइल गेम सभ में से एक हवे, PUBG मोबाइल, फोर्टनाइट बैटल रोयाल आ कॉल ऑफ ड्यूटी: मोबाइल के पीछे। ई खासतौर पर लैटिन अमेरिका, भारत आ दक्खिन-पूरबी एशिया में लोकप्रिय बा। फ्री फायर 2018 के चउथा तिमाही में [[गूगल प्ले स्टोर]] पर चउथा सभसे ढेर डाउनलोड भइल गेम रहल आ 2018 में दुनिया भर में आईओएस आ गूगल प्ले स्टोर पर एक साथ चउथा सभसे ढेर डाउनलोड भइल गेम रहल। ई टाइटिल 2018 में लगभग 182 मिलियन डाउनलोड कइलस, जेकरा चलते ई दुसरा सभसे ढेर डाउनलोड कइल गइल बैटल रॉयल मोबाइल गेम (फोर्टनाइट बैटल रॉयल से ऊपर आ खाली PUBG मोबाइल के पीछे) बन गइल, आ दिसंबर 2018 ले लगभग 19.3 मिलियन डॉलर के महीना के आमदनी भइल, जेकरा चलते ई एगो महत्वपूर्ण बित्तीय सफलता बन गइल गरेना के ह। अगस्त 2021 तक ले, गरेना फ्री फायर के गूगल प्ले स्टोर पर एक अरब से ढेर डाउनलोड भइल बा।
Q1 2021 के अंत में, फ्री फायर अमेरिका में राजस्व में PUBG मोबाइल के पीछे छोड़ दिहलस, जवना से 10 करोड़ डॉलर के कारोबार भइल जबकि PUBG मोबाइल के 68 मिलियन डॉलर के कारोबार भइल। पिछला साल के एही अवधि का मुकाबले फ्री फायर के आमदनी में साढ़े चार गुना के उछाल आइल.<ref name = "IGN1">{{cite web | url = https://www.ign.com/m/google/68894/news/the-best-games-e-applications-de-2018-sec-o-google | title = The best games and apps of 2018 according to Google | date = 3 December 2018 | access-date = 26 September 2019}}{{dead link|date=August 2021|bot=medic}}{{cbignore|bot=medic}}</ref><ref>{{Cite web | url = https://www.mobilegamer.com.br/2018/12/free-fire-eo-melhor-game-de-2018-on-popular-vote-in-some-countries.html | title = Free Fire is the best popular vote game of 2018? Only in some countries! | work = Mobile Gamer | date = 3 December 2018 | access-date = 26 September 2019 }}{{Dead link|date=October 2019 |bot=InternetArchiveBot |fix-attempted=yes }}</ref>
नवंबर 2019 ले फ्री फायर के दुनिया भर में 1 बिलियन डॉलर से ढेर के कमाई हो चुकल रहे। आगे चल के ई 2.13 बिलियन डॉलर के साथ 2020 के चउथा सभसे ढेर कमाई करे वाला वीडियो गेम बन गइल आ फिर 2021 के आठवाँ सभसे ढेर कमाई करे वाला मोबाइल गेम बनल जेह में 1.2 बिलियन डॉलर के कमाई भइल, कुल मिला के 2021 ले कम से कम 4.33 बिलियन डॉलर के कमाई भइल।
2020 आ 2021 में फ्री फायर के ईस्पोर्ट्स अवार्ड में ईस्पोर्ट्स मोबाइल गेम ऑफ द ईयर के अवार्ड मिलल।
== ईस्पोर्ट् ==
=== फ्री फायर वर्ल्ड सीरीज़ ===
फ्री फायर 2019 में "'''फ्री फायर वर्ल्ड सीरीज (FFWS)'''" ईस्पोर्ट्स प्रतियोगिता शुरू कइलस। 2020 में कोविड-19 महामारी के कारण FFWS के जगह क्षेत्रीय इवेंट के एगो सिलसिला, "फ्री फायर कॉन्टिनेंटल सीरीज" (FFCS) ले लिहल गइल। फरवरी 2021 में गैरेना 2 मिलियन डॉलर के इनाम के पूल के साथ फ्री फायर वर्ल्ड सीरीज (FFWS) के घोषणा कइलें।<ref>{{Cite news|date=31 May 2021|title=Phoenix Force Lifts The Winner's Trophy at Finals of Free Fire World Series 2021|work=[[News 18]]|url=https://www.news18.com/news/sports/phoenix-force-lifts-the-winners-trophy-at-finals-of-free-fire-world-series-2021-3795965.html|access-date=16 September 2021}}</ref>
साल 2021 में, फ्री फायर वर्ल्ड सीरीज इतिहास में सभसे ढेर देखल जाए वाला ईस्पोर्ट्स इवेंट बन गइल जेह में 54 लाख से ढेर पीक लाइव दर्शक लोग रहल आ ई लीग ऑफ लेजेंड्स वर्ल्ड चैम्पियनशिप के पीछे छोड़ दिहलस जेह में 51 लाख पीक व्यूअर रहलें।<ref>{{Cite web |first=Adrian |last=Lai |date=2021-06-03 |title=Free Fire World Series 2021 Singapore Smashes Global Viewership Record |url=https://sea.ign.com/free-fire-android/172569/news/free-fire-world-series-2021-singapore-smashes-global-viewership-record |access-date=2022-10-17 |website=IGN Southeast Asia |language=en-sg}}</ref><ref>{{Cite web |last=Daniels |first=Tom |date=2021-12-22 |title=Top 10 highest viewed esports events of 2021 |url=https://esportsinsider.com/2021/12/highest-viewed-esports-events-2021 |access-date=2022-10-21 |website=Esports Insider |language=en-GB}}</ref><ref>{{Cite web |last=Esguerra |first=Tyler |date=2022-11-06 |title=Worlds 2022 broke the tournament's peak viewership record by a huge margin |url=https://dotesports.com/league-of-legends/news/worlds-2022-broke-the-tournaments-peak-viewership-record-by-a-huge-margin |access-date=2022-11-13 |website=Dot Esports |language=en-US}}</ref>
=== क्षेत्रीय आयोजन ===
"'''EMEA इनविटेशनल'''" यूरोप, मध्य पूर्व आ अफिरका खातिर 2021 में फ्री फायर वर्ल्ड सीरीज के जगह रहल। एह चैम्पियनशिप में कुल 12 गो टीम शामिल रहली सँ. 200,000 डॉलर के पुरस्कार पूल के संगे।<ref>{{Cite web |date=2021-11-24 |title=Free Fire EMEA Invitational Finals 2021: Everything you need to know |url=https://esportsgen.com/free-fire-emea-invitational-finals-2021-everything-you-need-to-know-about-the-event/ |access-date=2022-10-15 |website=EsportsGen |language=en-US |archive-date=2022-10-15 |archive-url=https://web.archive.org/web/20221015034207/https://esportsgen.com/free-fire-emea-invitational-finals-2021-everything-you-need-to-know-about-the-event/ |url-status=dead }}</ref>
फ्री फायर एशिया चैम्पियनशिप एशिया खातिर 2021 में फ्री फायर वर्ल्ड सीरीज के जगह रहल। एह चैम्पियनशिप में वियतनाम, इंडोनेशिया, थाईलैंड, [[भारत]], [[ताइवान]], पाकिस्तान, आ एमसीपी (मलेशिया, कंबोडिया, आ पाकिस्तान) के कुल 31 गो टीम शामिल रहली सँ. टीमन के मुकाबला चार लाख डॉलर के पुरस्कार पूल खातिर बा.<ref>{{Cite web |date=2021-11-18 |title=Free Fire Asia Championship (FFAC) Play-ins will commence on 20 November! |url=https://esportz.in/free-fire-asia-championship-ffac-play-ins-will-commence-on-20-november |access-date=2022-10-15 |website=esportz |language=en |archive-date=2022-10-15 |archive-url=https://web.archive.org/web/20221015034202/https://esportz.in/free-fire-asia-championship-ffac-play-ins-will-commence-on-20-november |url-status=dead }}</ref>
लिगा ब्राजीलेरा डी फ्री फायर (LBFF) ब्राजील में मुख्य फ्री फायर ईस्पोर्ट्स इवेंट हवे। 3 गो डिवीजन, सीरीज ए, बी, आ सी से बनल ई साल में दू गो स्टेज में खेलल जाला आ अभिजात वर्ग फ्री फायर वर्ल्ड सीरीज खातिर क्वालीफाई करे ला।<ref>{{Cite web |title=Garena anuncia Liga Brasileira de Free Fire e Mundial 2020 no Brasil |url=https://www.techtudo.com.br/noticias/2020/01/garena-anuncia-liga-brasileira-de-free-fire-e-mundial-2020-no-brasil-esports.ghtml |access-date=2022-10-15 |website=TechTudo |language=pt-br}}</ref>
== विवाद ==
जनवरी 2022 में, [[पबजी|"PUBG"]] के डेवलपर '''Krafton''', Garena आ एकरे मूल कंपनी '''Sea''' के खिलाफ कॉपीराइट उल्लंघन के मुकदमा दायर कइलें। मुकदमा में Garena पर आरोप लगावल गइल बा कि ऊ अपना गेम गैरेना फ्री फायर आ फ्री फायर मैक्स में इन-गेम आइटम, गेम मैकेनिक्स आ पबजी: बैटलग्राउंड्स आ पबजी मोबाइल के समग्र रूप आ फीलिंग के नकल कइले बाड़न. क्राफ्टन के अनुसार, "फ्री फायर आ फ्री फायर मैक्स बैटलग्राउंड्स के कई पहलु सभ के व्यापक रूप से कॉपी करे ला, अलग-अलग आ संयोजन दुनों में, जवना में बैटलग्राउंड्स के कॉपीराइट वाला अनोखा गेम ओपनिंग 'एयर ड्रॉप' फीचर, गेम के संरचना आ खेल, हथियार सभ के संयोजन आ चयन, कवच, आ बिसेस चीज, लोकेशन आ रंग योजना, सामग्री आ बनावट के समग्र चुनाव।" <ref>{{Cite web|title=PUBG’s developer is suing Apple, Google, and the developer of lucrative PUBG lookalike Free Fire|url=https://www.theverge.com/2022/1/13/22882796/pubg-developer-krafton-suing-apple-google-garena-free-fire-max|access-date=2022-02-16|website=www.theverge.com|language=en}}</ref>
14 फरवरी 2022 के '''भारत सरकार के इलेक्ट्रॉनिक्स आ सूचना प्रौद्योगिकी मंत्रालय''' 53 गो अउरी ऐप के साथे गरेना फ्री फायर पर रोक लगा दिहलस जवन भारत के सूचना प्रौद्योगिकी अधिनियम, संविधान के धारा '''69ए''' के तहत भारत के निजता आ सुरक्षा खातिर खतरा पैदा कइलस।<ref>{{Cite web|title=India bans over 50 more Chinese apps including Garena Free Fire|url=https://social.techcrunch.com/2022/02/13/india-ban-china-apps/|access-date=2022-02-15|website=TechCrunch|language=en-US|archive-date=2022-08-19|archive-url=https://web.archive.org/web/20220819215134/https://techcrunch.com/2022/02/13/india-ban-china-apps/|url-status=dead}}</ref><ref>{{Cite web|date=2022-02-19|title=Garena Free Fire Ban In India: Most Users Can Still Play Free Fire Game Despite Government Ban|url=https://www.news18.com/news/tech/garena-free-fire-ban-in-india-most-users-can-still-play-free-fire-game-despite-government-ban-4788092.html|access-date=2022-02-23|website=News18|language=en}}</ref>
== संदर्भ ==
{{reflist}}
== बाहरी कड़ी ==
;ऑफिशियल
* [http://ff.garena.com/index/en/ आधिकारिक वेबसाइट] {{Webarchive|url=https://web.archive.org/web/20220707174024/https://ff.garena.com/index/en/ |date=2022-07-07 }}
* [https://ff.garena.com/universe/en/ फ्री फायर यूनिवर्स वेबसाइट]
।
।
।
।
।
{{वीडियोगेम-आधार}}
[[श्रेणी:बीडियोगेम]]
[[श्रेणी:2017 के वीडियो गेम]]
[[श्रेणी:बैटल रॉयल गेम]]
[[श्रेणी:एंड्रॉयड (ऑपरेटिंग सिस्टम) के खेल]]
[[श्रेणी:आईओएस गेम]]
[[श्रेणी:मल्टीप्लेयर वीडियो गेम]]
bgicu86dnyq68rcl7ier8hs0ujku0y9
मूरभूत कर्तव्य
0
87592
803361
789499
2026-07-29T16:23:37Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803361
wikitext
text/x-wiki
'''मूरभूत कर्तव्यन''' के सौसे [[भारत के लोग|भारतीय नागरिकन]] के मूरभूत फरज के रुप मे व्याख्यायित कइल गइल बा के, देशभक्ति के भावना के प्रोत्साहित करऽस आ भारत के एकता अखण्डित राखऽस। [[भारतीय संविधान|संविधान]] के भाग चार - (क)<ref>{{cite web |title=List of Fundamental Duties- Part IVA, Article 51A |url=https://pendulumedu.com/general-awareness/what-are-fundamental-duties |website=PendulumEdu |language=en-us}}</ref> में निर्धारित ई कर्तव्य व्यक्ति आ राष्ट्र से संबंधित बा। [[नीति निर्देशक सिद्धांत]]<ref>{{cite web |title=Profile - Directive Principles Of State Policy - Know India: National Portal of India |url=https://knowindia.india.gov.in/profile/directive-principles-of-state-policy.php |website=knowindia.india.gov.in |publisher=}}</ref> के जइसे एह के अदालत तबले लागू नइखे कर सकत जबले संसद के कवनो अधिनियम में दोसरा तरह के प्रावधान ना होखे।
[[चित्र:Constitution_of_India_(calligraphic)_Cover.png|alt=Constitution of India (calligraphic) Cover|thumb|भारतीय संविधान के ग्रन्थावरण (मूर सुलेख आ प्रकाशित संस्करण)।]]
==इतिहास==
नागरिकन के मूरभूत कर्तव्यन के संविधान में 1976 में [[42वा संविधान संशोधन]]<ref>{{cite web |last1=Patil |first1=Amruta |title=42nd Amendment Act |url=https://prepp.in/news/e-492-42nd-amendment-indian-polity-notes |website=prepp.in |publisher=prepp |access-date=Jul 11, 2022}}</ref> में स्वर्ण सिंह समिति<ref>{{cite web |title=Fundamental Dutie's list |url=https://unacademy.com/content/bpsc/study-material/polity/fundamental-duties/ |website=Unacademy}}</ref> के प्रस्ताव पर जोड़ल गइल। ई समिति के निर्माण ओह साल के सुरुआत में सरकार द्वारा कइल गइल रहे। सुरूआत में संविधान में मूर रूप से दसगो के संख्या में मूरभूत कर्तव्य रहे। बाद में 2002 के 86वा संविधान संशोधन द्वारा बढ़ा के ईगारेगो कर देवल गइल।
ई ईगारऽगो कर्तव्यन के संविधान के अनुच्छेद 51- क (भाग-IV-A में एकमात्र अनुच्छेद) में सूचीबद्ध बा। [[नीति निर्देशक सिद्धांत]]<ref>{{cite book |title=India 2020: Reference Annual |date=2020 |publisher=Publications Division, Ministry of Information and Broadcasting, Government of India |isbn=978-81-230-3239-9 |url=https://books.google.co.in/books/about/India_2020.html?id=Yh53zQEACAAJ&redir_esc=y |language=en}}</ref> के जैसे मूरभूत कर्तव्यो गैर न्यायसंगत नेत के होला।
2003 में [[भारत के सुप्रीम कोर्ट|सर्वोच्च न्यायालय]] के रंगनाथ मिश्र के फैसला दिहले के, "मूरभूत कर्तव्य के खाली कानूनी रूप से अमल ना होखे के चाही बलुक सामाजिक रूपो से अमल होखे के चाही।" एम्स विद्यार्थी संघ बिरुद्ध एम्स 2001<ref>{{cite web |title=.I.I.M.S. Students Union vs A.I.I.M.S. |url=https://indiankanoon.org/doc/1496684/}}</ref> मामला में सर्वोच्च न्यायालय के कहनामा रहे के, "मूरभूत कर्तव्य [[मूरभूत अधिकार]] के बराबरे महत्वपूर्ण बा।"
==महत्व==
मूरभूत कर्तव्य खाली पेडेंट भा तकनीकी नइखे सामाजिक परिवर्तन के कुंजी<ref>{{cite web |title=Fundamental Duties |url=https://www.drishtiias.com/daily-updates/daily-news-analysis/fundamental-duties-3 |website=Drishti IAS |publisher=DAILY UPDATES |language=en}}</ref> के रूप में शामिल कइल गइल रहे। [[समाज]] में अर्थपूर्ण योगदान देवे खातिर नागरिक के सबसे पहिले संविधान आ ओकरा अंगन के बुझे के होई। ई जरूरी बा के लोग एह बेवास्था अउरी एकर बारीकियन, ताकत आ मर्यादा के बुझस। एही से भारत में संवैधानिक संस्कृति के फैलाव करऽल बहुते जरूरी बा<ref>{{cite web |title=मौलिक कर्तव्य |url=https://www.drishtiias.com/hindi/daily-updates/daily-news-analysis/fundamental-duties-3 |website=Drishti IAS |language=en}}</ref>। हरेक नागरिकन के भारतीय लोकतंत्र में अर्थपूर्ण भागीदार बने के जरूरत बा आ संवैधानिक तत्व के ओकरा अस्सल भावना में आत्मसात करे के कोशिश करे के जरूरत बा। मूरभूत कर्तव्यन के उचित एहसास, संपूर्ण संचालन आ अमलीकरण खातिर एगो एकरूप नीति के जरूरत बा जवन नागरिकन के जवाबदेह बनावे में सहैया होखे।
मूरभूत कर्तव्य नागरिकन के फरजीयात करेला के, ऊ संविधान समेत भारत के राष्ट्रीय प्रतीकन के सम्मान करस, ओकर धरोहर के सञ्जोवह, ओकर [[वैविध्यपूर्ण संस्कृति]] के संरक्षित करस आ ओकर रक्षा में सहायता करस। एकरे अलावा ई सगरो भारतीयन के बीच भाईचारा के भावना के बढ़ावा देवे, [[पर्यावरण]] आ सार्वजनिक संपत्ति के रक्षा करे, वैज्ञानिक बिचार बिकसित करे, हिंसा से परहेज करे आ जीवन के हर क्षेत्र में श्रेष्ठता के ओरीया प्रयास करे के फरजीयात करे लें।
[[राष्ट्रपति]], [[उपराष्ट्रपति]], [[सभापति]], [[संसद सदस्य]], विधानसभा के सदस्य आदि समेत कवनो नागरिक द्वारा संविधान में निगमित मूरभूत कर्तव्य के उल्लंघन के स्थिति में ई संविधान के तिरस्कार समान बा, जे राष्ट्रीय गौरव के अपमान निवारण अधिनियम 1971 के बदे दंडयोग्य बा, [[भारतीय सर्वोच्च न्यायालय|सर्वोच्च न्यायालय]] ई फैसला दिहऽले के ई मूरभूत कर्तव्य [[विधानसभा]] से पारित भइल कवनो कानून के संवैधानिकता के निर्धारितो करे में अदालत के सहायता कर सकेला। अंतर्राष्ट्रीय संधियन में अयीसन कर्तव्यन के संदर्भ बा जैसे के मानवाधिकार के सार्वत्रिक घोषणा आ नागरिक आ राजनीतिक अधिकारन के अंतर्राष्ट्रीय करार आ अनुच्छेद 51 - (क)<ref>{{cite web |last1=Hemant |first1=Singh |title=What is Article 15 of the Indian Constitution? Important Features and Provisions |url=https://www.jagranjosh.com/general-knowledge/article-15-1561544384-1 |website=Jagranjosh.com |access-date=12 may 2020 |date=12 मई 2020}}</ref> में [[भारतीय संविधान]] के एह संधिन के अनुरूप बनावल गइल बा।
==लेखा==
हरेक भारतीय प्रजा के ई कर्तव्य होखी के<ref>{{cite web |title=मूल कर्तव्य एक परिचय |url=https://sugamgyan.com/post/fundamental-duties/ |website=सुगम ज्ञान |language=hi-IN |date=15 सितंबर 2018 }}{{Dead link|date=March 2026 |bot=InternetArchiveBot |fix-attempted=yes }}</ref>;
1. संविधान के पालन करस आ संविधान में समायेल आदर्श के आ राष्ट्रीय संस्थानन, [[जन गण मन|राष्ट्रगान]], [[तिरंगा|राष्ट्रपताका]] के सम्मान करस।
2. स्वतंत्रताला आपन [[भारत के आजादी आंदोलन|राष्ट्रीय आंदोलनन]] के प्रेरणा देवेवाला ऊँच आदर्श के सम्मान करस आ अनुसरण करस।
3. भारत के [[संप्रभुता|परमाधिपत्यकता]], एकता आ अखण्डिता के अखण्डरुप से रक्षा करस आ एहर जतन करस।
4. [[भारत|भारतराष्ट्र]] के सुरक्षा करस आ आवाहन कइला पर राष्ट्र के सेवा करस।
5. भारत के सौसे [[भारत के लोग|प्रजाजन]] में समरसता आ समान बन्धुत्ता के भाव के निर्माण करस जौन कवनो [[धर्म|धम्म]], [[भाषा]] आ कवनो प्रदेश चाहे संप्रदाय पर आधारित सगरो भेदभाव से उप्पर होखो। स्त्रीयन के गौरव के अपमानीत करे ऐसन बेवहार के तेज देवस।
6. आपन गौरवशाली परम्परा के आ समृद्ध [[भारतीय संस्कृति|संस्कृतियन]] के मिनऽहा के बुझस आ एह के सहेजस।
7. [[जंगल|जङल]], पोखरन, [[नदी|सरितन]] आ [[जंगली जीव|आरण्य पशुपंछीयन]] समेत कुदरती [[पर्यावरण]] के जतन आ बिकास करस, आऊर जीव - जंतुवन के बदे दायाभाव रखस।
8. वैज्ञानिक आपाधापी, मानवता आ जिज्ञासा आ सुधारशिलता के भावना बिक्सित करस।
9. प्रजात्व संपाति के रक्षा करस आ हिंसा के तिरस्कार करस।
10. बेक्तिगत आ सामूहिक गतिबिधियन के सगरो बिस्तार में निरन्तम् प्रगति के प्रयास करस जेसे राष्ट्र निरन्तम् प्रयत्न आ उपलब्धि के नाया ऊँचाईयन के टो सको।
11. जदि प्रजा माई - बाबू चाहे गार्जियन होखस तऽ छव से चौदऽ बरीस के आपन सबिता चाहे पाल्यसबिता के शिक्षा के तक प्रदान करावस।
==इहो देखल जाय==
* [[भारतीय संविधान के आमुख]]
* [[भारतीय संविधान]]
== संदर्भ ==
{{Reflist|33em}}
1jpfgbroieqckwdvi1vohjd4vob3omo
गुरु तेग बहादुर
0
87743
803372
766293
2026-07-29T16:24:33Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803372
wikitext
text/x-wiki
{{Infobox religious biography
| religion = [[सिख धर्म]]
| name = गुरु तेग बहादुर
| image = Portrait_of_Guru_Tegh_Bahadur_by_painter_Ahsan.jpg
| alt =
| caption = बिचला-17वीं-सदी के पोर्ट्रेट, चित्रकार: अहसान
| birth_name = त्याग मल
| birth_date = {{Birth-date| 21 अप्रैल 1621}}
| birth_place = [[अमृतसर]], [[लाहौर सूबा]], [[मुगल राज|मुगल साम्राज्य]] <br /> {{small|(वर्तमान पंजाब, भारत)}}
| death_date = {{Death-date and age|24 नवंबर 1675| 1 मई 1621}}
| death_place = [[दिल्ली]], [[मुगल राज|मुगल साम्राज्य]] <br /> {{small|(वर्तमान [[भारत]])}}
| death_cause = सिर काट के मृत्युदंड
| period = 1665–1675
| known_for = * गुरुग्रंथ साहिब के श्लोक
* [[औरंगजेब]] के आदेशानुसार मृत्युदंड<ref name="pslf" /><ref>Gill, Sarjit S., and Charanjit Kaur (2008), "Gurdwara and its politics: Current debate on Sikh identity in Malaysia", SARI: Journal Alam dan Tamadun Melayu, Vol. 26 (2008), pages 243–255, Quote: "Guru Tegh Bahadur died in order to protect the freedom of India from invading Mughals."</ref><ref name=cs2013/><ref name=sg2007>{{cite book|last=Gandhi|first=Surjit|title=History of Sikh gurus retold|publisher=Atlantic Publishers|year=2007|isbn=978-81-269-0858-5|pages=653–91}}</ref>
* [[आनंदपुर साहिब]] के संस्थापक
| spouse = [[माता गुजरी]]
| children = [[गुरु गोबिंद सिंह]]
| parents = [[गुरु हरगोबिंद]] आ [[माता नानकी]]
| predecessor = [[गुरु हरकिशन]]
| successor = [[गुरु गोबिंद सिंह]]
| image_upright =
| battles = [[करतारपुर के लड़ाई]]
| native_name = ਗੁਰੂ ਤੇਗ਼ ਬਹਾਦਰ
| native_name_lang = pa
| native name lang = pa
| signature = Authentic signature (neeshan) of Guru Tegh Bahadur.jpg
}}
[[चित्र:Guru_Teg_bahadur_ji.jpg|thumb|456x456px| [[गुरुद्वारा शीशगंज साहिब|गुरद्वारा सिस गंज साहब]] के आंतरिक दृश्य]]
'''गुरु तेग बहादुर''' ( [[पंजाबी भाषा|पंजाबी]] : ਗੁਰੂ ਤੇਗ਼ ਬਹਾਦਰ <small>( गुरमुखी )</small> ; 1 अप्रैल 1621 – 11 नवंबर 1675) <ref>{{Cite web|url=http://www.sikhs.org/guru9.htm|title=The Ninth Master Guru Tegh Bahadur (1621–1675)|website=sikhs.org|archive-url=https://web.archive.org/web/20190107050930/http://www.sikhs.org/guru9.htm|archive-date=7 January 2019|access-date=23 November 2014}}</ref> दस गो गुरु लोग में से नौवाँ गुरु रहलें जे [[सिख धर्म]] के स्थापना कइलें आ 1665 से ले के 1675 में सिर काट दिहला ले सिख लोग के नेता रहलें। इनके जनम भारत के पंजाब के [[अमृतसर]] में 1621 में भइल आ छठवाँ सिख गुरु [[गुरु हरगोबिंद साहब के बा|गुरु हरगोबिंद]] के छोट बेटा रहलें। सिद्धांतवादी आ निर्भीक योद्धा मानल जाए वाला ऊ एगो आध्यात्मिक विद्वान आ कवि रहलें जिनके 115 गो स्तोत्र सिख धर्म के मुख्य ''[[गुरु ग्रंथ साहिब|ग्रंथ श्री गुरु ग्रंथ साहब]]'' में शामिल बाड़ें।
गुरु तेग बहादुर के छठवाँ [[मुगल राज|मुगल]] सम्राट [[औरंगजेब]] के आदेश पर भारत के [[दिल्ली]] में मृत्यदंड दिहल गइल। <ref name="bbcgtb">{{Cite web|url=http://www.bbc.co.uk/religion/religions/sikhism/people/teghbahadur.shtml|title=Religions – Sikhism: Guru Tegh Bahadur|publisher=[[BBC]]|archive-url=https://web.archive.org/web/20170414075330/http://www.bbc.co.uk/religion/religions/sikhism/people/teghbahadur.shtml|archive-date=14 April 2017|access-date=20 October 2016}}</ref> सिख पवित्र परिसर [[गुरुद्वारा शीशगंज साहिब|गुरुद्वारा सिस गंज साहब]] आ दिल्ली के [[गुरद्वारा रकाब गंज साहब]] गुरु तेग बहादुर के मूंडी काटल जाये आ दाह संस्कार के जगहन के चिन्हित करेलें। उनकर शहादत के हर साल 24 नवम्बर ''के गुरु तेग बहादुर के शहीदी दिवस के'' रूप में याद कइल जाला।
== जीवनी ==
=== शुरुआती जिनगी ===
गुरु तेग बहादुर छठवाँ गुरु गुरु हरगोबिंद के सबसे छोट बेटा रहलें: गुरु हरगोबिंद के एगो बेटी बीबी वीरो आ पाँच गो बेटा बाबा गुरदित, सूरज माल, अनी राय, अटल राय, आ त्यागा माल भइलें। त्यागा माल के जनम अमृतसर में 1 अप्रैल 1621 के सबेरे भइल रहे। इनके तेग बहादुर (तलवार के पराक्रमी) नाँव से जानल जाए लागल, ई नाँव गुरु हरगोबिंद द्वारा मुगल लोग के खिलाफ लड़ाई में आपन शौर्य देखवला के बाद दिहल गइल।
ओह घरी अमृतसर सिख आस्था के केंद्र रहे। सिख गुरु लोग के आसन के रूप में, आ ''मसंद'' भा मिशनरी लोग के कनेक्शन के माध्यम से देश के दूर-दूर के इलाका में सिख लोग से जुड़ाव के साथ, एकरा में सिख धरम के राजधानी के विशेषता के बिकास भइल रहे।
गुरु तेग बहादुर के पालन पोषण सिख संस्कृति में भइल रहे आ तीरंदाजी आ घुड़सवारी के प्रशिक्षण मिलल रहे। उहाँ के [[वेद]], [[उपनिषद]], आ [[पुराण]] जइसन पुरान क्लासिक भी सिखावल गइल रहल। तेग बहादुर के बियाह 3 फरवरी 1632 के माता गुजरी से भइल।
=== बकाला में निवास ===
1640 के दशक में गुरु हरगोबिंद आ उनकर मेहरारू नानकी तेग बहादुर आ माता गुजरी के साथे मिल के [[अमृतसर जिला]] के अपना पैतृक गाँव बकाला में आ गइलें। बकाला, जइसन कि ''गुरबिलास दसवीं पातशाही'' में वर्णित बा, तब एगो समृद्ध शहर रहे जवना में कई गो सुंदर कुंड, इनार, आ ''बाओली'' रहे। गुरु हरगोबिंद के निधन के बाद तेग बहादुर अपना पत्नी आ महतारी के संगे बकला में रहत रहले।
=== गुरु जी के सफर ===
मार्च 1664 में गुरु हरकिशन के मतहाई (चेचक) हो गइल। जब उनकर अनुयायी लोग पूछल कि उनकरा बाद के के नेतृत्व करी त उ जवाब देले कि ''बाबा बकला'', मतलब कि उनकर उत्तराधिकारी बकला में मिले वाला बा। मरत गुरु के शब्दन में अस्पष्टता के फायदा उठावत बहुत लोग अपना के नया गुरु बतावत बकला में स्थापित हो गइल। एतना दावेदार देख के सिख लोग अचरज में पड़ गइलें।
एगो कहानी के मोताबिक, 8वें गुरु के बाद गुरु हरकिशन साहब जी स्वर्ग धाम में जाये के बाद, गुरु के रूप में नकली कई लोग अस्थापित भ गइल रहल। एगो धनी व्यापारी बाबा मखान शाह लबाना, एक बेर आपन नाव के सुरक्षित समुंदर पार करे के प्रार्थना कइले रहले आ वादा कइले रहले कि अगर ऊ बच गइलन त सिख गुरु के 500 सोना के सिक्का उपहार में दे दीहें।<ref name="mk1992" /> उ नौवां गुरु के खोज में पहुंचले। उ एक दावेदार से दूसरा दावेदार तक प्रणाम करत अउरी हर गुरु के दुगो सोना के सिक्का चढ़वले, इ मानत कि सही गुरु के पता चल जाई कि उनुकर मौन वादा रहे कि उ अपना सुरक्षा खाती 500 सिक्का उपहार में दे दिहे। हर "गुरु" से मिलत सोना के दुनु सिक्का स्वीकार क के विदाई देत रहे। <ref name="mk1992" /> तब उनका पता चलल कि तेग बहादुर भी बकला में रहत रहले। लबाना तेग बहादुर के दू गो सोना के सिक्का के सामान्य प्रसाद उपहार में दिहलें। तेग बहादुर उनुका के आपन आशीर्वाद देले अउरी टिप्पणी कइले कि उनुकर प्रसाद वादा कइल पांच सौ से काफी कम बा। मखान शाह लबाना तुरते फरक बूझ के ऊपर भाग गइलन। ऊ छत से चिल्लाए लगले, "गुरु लधो रे, गुरु लधो रे" मतलब "गुरु मिल गइलें, गुरु मिल गइलें"।
अगस्त 1664 में एगो सिख संगत बकला पहुंचल आ तेग बहादुर के सिख लोग के नौवाँ गुरु नियुक्त कइलस। ''संगत'' के नेतृत्व गुरु तेग बहादुर के बड़ भाई दीवान दुर्गा मल, उनुका के गुरुत्व प्रदान कइले।
जईसे कि मुगल सम्राट जहांगीर के गुरु अर्जन के फांसी दिहला के बाद सिख लोग के बीच रिवाज रहे, गुरु तेग बहादुर के घेरले हथियारबंद अंगरक्षक रहल करें। उ खुद तपस्वी जीवन जीयत रहले।
== रचना ==
गुरु तेग बहादुर ग्रंथ साहब में कई गो भजन के योगदान दिहलें जेह में गुरु ग्रंथ साहब के अंत के लगे के हिस्सा के ''श्लोक'', या दोहा भी सामिल रहलें। गुरु तेग बहादुर मुगल साम्राज्य के बिबिध हिस्सा के दौरा कइलें आ गोबिंद सहली द्वारा महाली में कई गो सिख मंदिर बनावे के कहल गइल। इनके रचना सभ में 116 गो शबद, 15 गो राग आ इनके भगत सभ के 782 गो रचना सभ के श्रेय दिहल जाला जे सिख धर्म में ''बानी'' के हिस्सा हवें। इनके रचना सभ के ''[[गुरु ग्रंथ साहिब|गुरु ग्रंथ साहब]]'' (पन्ना 219-1427) में सामिल कइल गइल बा। एह में कई गो बिसय सभ के सामिल कइल जाला, जइसे कि भगवान के स्वभाव, मानवीय लगाव, तन, मन, दुख, मर्यादा, सेवा, मौत आ मुक्ति।
== सफर ==
गुरु तेग बहादुर पहिला सिख गुरु नानक के शिक्षा के प्रचार करे खातिर [[ढाका]] आ [[आसाम|असम]] समेत देश के अलग-अलग हिस्सा में बहुत घूमले। जवना जगहन पर उहाँ के घूमे आ ठहरल रहले, उहाँ सिख मंदिरन के स्थल बन गइल। अपना यात्रा के दौरान गुरु तेग बहादुर सिख विचार अउरी संदेश के प्रसार कइले, संगही सामुदायिक पानी के कुआं अउरी लंगर (गरीब खाती सामुदायिक रसोई के दान) शुरू कइले।
गुरु लगातार तीन बेर कीरतपुर के दौरा कइलेंl 21 अगस्त 1664 के गुरु तेग बहादुर बीबी रूप के पिता गुरु हर राय, सातवाँ सिख गुरु आ उनकर भाई गुरु हरकिशन के निधन पर सांत्वना देवे खातिर ओहिजा गइलें। <ref>{{Cite web|url=https://www.britannica.com/topic/Sikhism/Guru-Har-Rai|title=Sikhism - Guru Har Rai {{!}} Britannica|website=www.britannica.com|language=en|access-date=2021-11-26}}</ref> दूसरा यात्रा 15 अक्टूबर 1664 के भइल, 29 सितंबर 1664 के गुरु हर राय के महतारी बस्सी के निधन के समय। तीसरा यात्रा में उत्तर-पश्चिम भारतीय उपमहाद्वीप के माध्यम से काफी व्यापक यात्रा के समापन भइल| उनकर बेटा [[गुरु गोबिंद सिंह|गुरु गोविंद सिंह]], जे दसवाँ सिख गुरु होखीहें, के जनम [[पटना]] में भइल रहे, जब ऊ 1666 में असम के धुबरी में दूर रहले, जहाँ गुरद्वारा श्री गुरु तेग बहादुर साहब अस्थापित बाटे। उहाँ उ बंगाल के राजा राम सिंह अउरी अहोम राज्य (बाद में असम) के राजा चकरद्वाज के बीच युद्ध के खतम करे में मदद कइलें। उ मथुरा, आगरा, इलाहाबाद अउरी वाराणसी के दौरा कइलें।
असम, बंगाल आ बिहार के दौरा के बाद गुरु बिलासपुर के रानी चंपा के दौरा कइलें जवन गुरु के अपना राज्य के जमीन के टुकड़ा देवे के प्रस्ताव रखली। गुरु साहब 500 [[रुपिया|रुपया]] में जगह के खरीद लिहलेंl। उहाँ गुरु तेग बहादुर हिमालय के तलहटी में आनंदपुर साहब शहर के स्थापना कइलें।<ref name="bbcgtb">{{Cite web|url=http://www.bbc.co.uk/religion/religions/sikhism/people/teghbahadur.shtml|title=Religions – Sikhism: Guru Tegh Bahadur|publisher=[[BBC]]|archive-url=https://web.archive.org/web/20170414075330/http://www.bbc.co.uk/religion/religions/sikhism/people/teghbahadur.shtml|archive-date=14 April 2017|access-date=20 October 2016}}<cite class="citation web cs1" data-ve-ignore="true">[http://www.bbc.co.uk/religion/religions/sikhism/people/teghbahadur.shtml "Religions – Sikhism: Guru Tegh Bahadur"]. [[बीबीसी|BBC]]. [https://web.archive.org/web/20170414075330/http://www.bbc.co.uk/religion/religions/sikhism/people/teghbahadur.shtml Archived] from the original on 14 April 2017<span class="reference-accessdate">. Retrieved <span class="nowrap">20 October</span> 2016</span>.</cite></ref> 1672 में तेग बहादुर कश्मीर आ उत्तर-पश्चिम सीमा के रास्ता से होके, जनता से मिले खातिर यात्रा कइलें, काहें से कि गैर-मुसलमान लोग के उत्पीड़न नया ऊंचाई पर पहुँच गइल रहल।
== मृत्युदंड ==
[[चित्र:Head_of_Guru_Tegh_Bahadar_is_brought_to_Anandpur_by_Sikhs.jpg|thumb|गुरु तेग बहदर के सिर के सिख लोग द्वारा आनंदपुर ले आवल जा रहल फ्रेस्को कला]]
सिख कथन सभ के प्राथमिक बिबरन ''बचितर नाटक'', गुरु तेग बहादुर के बेटा गुरु गोविंद सिंह के संस्मरण बाटे जे 1680 के दशक के अंत से 1690 के दशक के अंत के बीच के हवे।<ref name=":0">{{Cite journal|last=Fenech|first=Louis E.|date=1997|title=Martyrdom and the Sikh Tradition|url=https://www.jstor.org/stable/606445|journal=Journal of the American Oriental Society|volume=117|issue=4|pages=623–642|doi=10.2307/606445|issn=0003-0279|jstor=606445|archive-url=https://web.archive.org/web/20181006000554/https://www.jstor.org/stable/606445|archive-date=6 October 2018|access-date=2 December 2017}}</ref> गुरु तेग बहादुर के बेटा आ उत्तराधिकारी गुरु के फाँसी के याद कइलें:
{{Quote|In this dark age, Tegh Bahadur performed a great act of chivalry (saka) for the sake of the frontal mark and sacred thread. He offered all he had for the holy. He gave up his head, but did not utter a sigh. He suffered martyrdom for the sake of religion. He laid down his head, but not his honor. Real men of God do not perform tricks like showmen. Having broken the pitcher on the head of the king of Delhi, he departed to the world of god. No one has ever performed a deed like him. At his departure, the whole world mourned, while the heavens hailed it as victory.|[[Guru Gobind Singh]]}}
तेग बहादुर के फाँसी के अउरी सिख बिबरन, सभ "भरोसेमंद सिख लोग के गवाही" से मिलल होखे के दावा करे लें, लगभग अठारहवीं सदी के अंत में ही उभरल शुरू भइल आ एह तरीका से, अक्सर परस्पर विरोधी बाड़ें। <ref name="sc2001">{{Cite web|url=http://www.thehindu.com/thehindu/2001/10/16/stories/05162524.htm|title=Guru Tegh Bahadur's martyrdom|last=Chandra|first=Satish|website=[[The Hindu]]|archive-url=https://web.archive.org/web/20020228135707/http://thehindu.com/thehindu/2001/10/16/stories/05162524.htm|archive-date=28 February 2002|access-date=20 October 2016}}</ref> इतिहासकार सोहन लाल सूरी के कहनाम बा कि गुरु के दक्षिणी पंजाब में 1672 से 1673 के बीच यात्रा के दौरान सैनिक अउरी घुड़सवार के हजारों अनुयायी मिलल अउरी मुगल प्रतिनिधि के प्रतिरोधक लोगन के आश्रय दिहल गइल। औरंगजेब के एह तरह के गतिविधि के बारे में चेतावल गइल, चिंता के कारण के रूप में जवना से संभवतः विद्रोह हो सकत रहे।
फारसी सूत्रन के कहनाम बा कि गुरु एगो डाकू रहलें जिनकर पंजाब के लूटपाट आ बलात्कार के साथे-साथे उनकर विद्रोही गतिविधियन के चलते उनकरा के मृत्यदंड के सजा जल्दी हो गइल। इनके मृत्युदंड के इतिहास बतावे वाला सभसे पहिले के ''फारसी'' स्रोत गुलाम हुसैन खान लगभग 1782 लिखल सियार-उल-मुत्ताखेरिन बाटे, जहाँ तेग बहादुर के (कथित) प्रजा के अत्याचार के कारण औरंगजेब के क्रोध पैदा भइल मानल जाला:<ref name="sc2001">{{Cite web|url=http://www.thehindu.com/thehindu/2001/10/16/stories/05162524.htm|title=Guru Tegh Bahadur's martyrdom|last=Chandra|first=Satish|website=[[The Hindu]]|archive-url=https://web.archive.org/web/20020228135707/http://thehindu.com/thehindu/2001/10/16/stories/05162524.htm|archive-date=28 February 2002|access-date=20 October 2016}}<cite class="citation web cs1" data-ve-ignore="true" id="CITEREFChandra">Chandra, Satish. [https://web.archive.org/web/20020228135707/http://thehindu.com/thehindu/2001/10/16/stories/05162524.htm "Guru Tegh Bahadur's martyrdom"]. ''[[दि हिंदू|The Hindu]]''. Archived from [http://www.thehindu.com/thehindu/2001/10/16/stories/05162524.htm the original] on 28 February 2002<span class="reference-accessdate">. Retrieved <span class="nowrap">20 October</span> 2016</span>.</cite></ref>
{{Quote|Tegh Bahadur, the Ninth successor of (Guru) Nanak became a man of authority with a large number of followers. (In fact) Several thousand persons used to accompany him as he moved from place to place. His contemporary Hafiz Adam, a faqir belonging to the group of Shaikh Ahmad Sirhindi's followers, had also come to have a large number of murids and followers. Both these men (Guru Tegh Bahadur and Hafiz Adam) used to move about in Punjab, adopting a habit of coercion and extortion. Tegh Bahadur used to collect money from Hindus and Hafiz Adam from Muslims. The royal ''waqia navis'' (news reporter and intelligence agent) wrote to the Emperor Alamgir [Aurangzeb] of their manner of activity, added that if their authority increased they could become even refractory.|Ghulam Husain|Siyar-ul-Mutakhkherin}}
हालांकि सतीश चंद्र हुसैन खान के तर्क के फेस वैल्यू से ना लेवे के चेतावनी देत बाड़े। <ref name="sc2001">{{Cite web|url=http://www.thehindu.com/thehindu/2001/10/16/stories/05162524.htm|title=Guru Tegh Bahadur's martyrdom|last=Chandra|first=Satish|website=[[The Hindu]]|archive-url=https://web.archive.org/web/20020228135707/http://thehindu.com/thehindu/2001/10/16/stories/05162524.htm|archive-date=28 February 2002|access-date=20 October 2016}}<cite class="citation web cs1" data-ve-ignore="true" id="CITEREFChandra">Chandra, Satish. [https://web.archive.org/web/20020228135707/http://thehindu.com/thehindu/2001/10/16/stories/05162524.htm "Guru Tegh Bahadur's martyrdom"]. ''[[दि हिंदू|The Hindu]]''. Archived from [http://www.thehindu.com/thehindu/2001/10/16/stories/05162524.htm the original] on 28 February 2002<span class="reference-accessdate">. Retrieved <span class="nowrap">20 October</span> 2016</span>.</cite></ref> ऊ अलीवर्दी खान के रिश्तेदार रहलें — औरंगजेब के सभसे करीबी विश्वासपात्र लोग में से एक — आ शायद "आधिकारिक औचित्य" देत रहलें। <ref name="sc2001" /><ref>{{Cite web|url=https://en.banglapedia.org/index.php?title=Siyar-ul-Mutakhkherin|title=Siyar-ul-Mutakhkherin – Banglapedia|website=en.banglapedia.org|archive-url=https://web.archive.org/web/20210918151706/https://en.banglapedia.org/index.php?title=Siyar-ul-Mutakhkherin|archive-date=18 September 2021|access-date=2021-09-18}}</ref> {{Efn|Chandra points out a factual error to justify his caution: Adam had died much earlier.}} उपर दिहल कथ्य के अउरी चुनौती भी बाड़ी सऽ। गुलाम हुसैन पंजाब से बहुत दूर रहत रहले। साथे साथ हाफिज आदम से गुरु के संगति कालक्रम में असंगति लिहले बा। तेग बहादुर के गुरु के दर्जा पावे से 21 साल पहिले ई. 1643 में मदीना में हाफिज आदम के निधन हो गइल रहल। आगे इहो बतावल जरूरी बा कि गुलाम हुसैन के मुताबिक तेग बहादुर ग्वालियर में बंद रहले, जहां शाही आदेश के तहत उनुकर लाश के "चार क्वार्टर में काट के" किला के चार गेट प लटका दिहल गइल जबकि इ सभके मालूम बा तेग बहादुर के दिल्ली में फांसी दिहल गइल जहाँ एह घरी सिसगंज गुरुद्वारा बसल बा।{{प्रमाण देईं|date=May 2022}} अठारहवीं सदी के दौरान लिखल गइल सिख साखी सब अप्रत्यक्ष रूप से फारसी स्रोत सभ में कथ्य के समर्थन करे ला; कुछुओ ना कि औरंगजेब के लागू हठधर्मी नीति के जवाब में गुरु "देश के मुस्लिम शासकन के हिंसक विरोध" में रहले। फारसी अउरी सिख दुनों सूत्र एह बात से सहमत बाड़े कि तेग बहादुर सैन्य रूप से मुगल राज्य के विरोध कइले रहले अउरी एह से औरंगजेब के राज्य के दुश्मन के सजा देवे के जोश के मुताबिक मृत्युदंड के निशाना बनावल गइल रहे।
कई गो विद्वान लोग एह कथ्य के पहचान निम्नलिखित बा: कश्मीर से हिन्दू पंडित लोग के एगो मंडली औरंगजेब के दमनकारी नीतियन के खिलाफ मदद के निहोरा कइलस, जवना पर गुरु तेग बहादुर ओह लोग के अधिकार के रक्षा करे के फैसला कइलें। मुगल अधिकारियन का तरफ से कश्मीरी ब्राह्मणन पर होखे वाला अत्याचार के सामना करे खातिर तेग बहादुर मखौवाल के निवास से निकल गइलन बाकिर रोपड़ में गिरफ्तार कर के सिरहिंद में जेल में डाल दिहल गइलें चार महीना बाद नवंबर 1675 में उनकरा के दिल्ली ट्रांसफर कइल गइल आ भगवान के नजदीकी साबित करे भा इस्लाम अपनावे खातिर चमत्कार करे के कहल गइल।<ref name="Grewal1998p71" /> गुरु साहब मना कर दिहलन आ उनका साथे धराइल उनकर तीन साथी लोग के उनही के सामने यातना दिहल गइल: भाई माटी दास के टुकड़ा-टुकड़ा में आरी से काटल गइल, भाई दयाल दास के उबलत पानी के कड़ाही में फेंक दिहल गइल, आ भाई सती दास के जिंदा जरा दिहल गइल।<ref name="Grewal1998p71" /> ओकरा बाद लाल किला के नजदीक बाजार चौक चांदनी चौक में तेग बहादुर के सरेआम सिर काट दिहल गइल।<ref name="Grewal1998p71" />
सतीश चंद्र एह बिषद-कथावन के प्रामाणिकता पर संदेह जतवले बाड़न, जवन चमत्कार पर केंद्रित बा — औरंगजेब एहसभ में बिस्वास करे वाला ना रहलें। आगे ऊ सिख विवरण के भीतर कश्मीर में हिन्दू लोग के उत्पीड़न के कथ्य से संबंधित संदेह जतवले बाड़न, एह बात के टिप्पणी करत कि कवनो समकालीन स्रोत में ओहिजा के हिन्दू लोग के उत्पीड़न के जिक्र नइखे भइल।<ref name="sc2001">{{Cite web|url=http://www.thehindu.com/thehindu/2001/10/16/stories/05162524.htm|title=Guru Tegh Bahadur's martyrdom|last=Chandra|first=Satish|website=[[The Hindu]]|archive-url=https://web.archive.org/web/20020228135707/http://thehindu.com/thehindu/2001/10/16/stories/05162524.htm|archive-date=28 February 2002|access-date=20 October 2016}}<cite class="citation web cs1" data-ve-ignore="true" id="CITEREFChandra">Chandra, Satish. [https://web.archive.org/web/20020228135707/http://thehindu.com/thehindu/2001/10/16/stories/05162524.htm "Guru Tegh Bahadur's martyrdom"]. ''[[दि हिंदू|The Hindu]]''. Archived from [http://www.thehindu.com/thehindu/2001/10/16/stories/05162524.htm the original] on 28 February 2002<span class="reference-accessdate">. Retrieved <span class="nowrap">20 October</span> 2016</span>.</cite></ref> लुईस फेनेच प्राथमिक स्रोत सभ के कमी के आलोक में, कौनों फैसला पास करे से इनकार करे लें; हालाँकि, ऊ नोट करे लें कि एह सिख बिबरन सभ में घटना सभ में शहादत के लिखित कइल गइल रहे, एकर मकसद पाठकन में आघात से बेसी गर्व पैदा कइल रहे। आगे उनकर तर्क बा कि तेग बहादुर अपना आस्था खातिर खुद के बलिदान देले रहले; बचितर नाटक में अपना के जिक्र करत अंश में जंजू आ तिलक। <ref name=":0">{{Cite journal|last=Fenech|first=Louis E.|date=1997|title=Martyrdom and the Sikh Tradition|url=https://www.jstor.org/stable/606445|journal=Journal of the American Oriental Society|volume=117|issue=4|pages=623–642|doi=10.2307/606445|issn=0003-0279|jstor=606445|archive-url=https://web.archive.org/web/20181006000554/https://www.jstor.org/stable/606445|archive-date=6 October 2018|access-date=2 December 2017}}<cite class="citation journal cs1" data-ve-ignore="true">Fenech, Louis E. (1997). [https://www.jstor.org/stable/606445 "Martyrdom and the Sikh Tradition"]. ''Journal of the American Oriental Society''. '''117''' (4): 623–642. [[डिजिटल चीज पहिचानक|doi]]:[[doi:10.2307/606445|10.2307/606445]]. [[आइएसएसएन|ISSN]] [//www.worldcat.org/issn/0003-0279 0003-0279]. [[जेस्टोर|JSTOR]] [//www.jstor.org/stable/606445 606445]. [https://web.archive.org/web/20181006000554/https://www.jstor.org/stable/606445 Archived] from the original on 6 October 2018<span class="reference-accessdate">. Retrieved <span class="nowrap">2 December</span> 2017</span>.</cite></ref>
उल्लेखनीय बा कि सिख साहित्य में एह दबंग विषय के विपरीत आधुनिकता से पहिले के कुछ सिख विवरणन में एकर दोष एगो कटु उत्तराधिकार विवाद पर डालल गइल रहे: गुरु हरकिशन के बड़ भाई राम राय के ई सुझाव दे के औरंगजेब के तेग बहादुर के खिलाफ भड़कावे के मानल गइल ऊ दरबार में चमत्कार करके आपन आध्यात्मिक महानता साबित करेला। <ref name="sc2001">{{Cite web|url=http://www.thehindu.com/thehindu/2001/10/16/stories/05162524.htm|title=Guru Tegh Bahadur's martyrdom|last=Chandra|first=Satish|website=[[The Hindu]]|archive-url=https://web.archive.org/web/20020228135707/http://thehindu.com/thehindu/2001/10/16/stories/05162524.htm|archive-date=28 February 2002|access-date=20 October 2016}}<cite class="citation web cs1" data-ve-ignore="true" id="CITEREFChandra">Chandra, Satish. [https://web.archive.org/web/20020228135707/http://thehindu.com/thehindu/2001/10/16/stories/05162524.htm "Guru Tegh Bahadur's martyrdom"]. ''[[दि हिंदू|The Hindu]]''. Archived from [http://www.thehindu.com/thehindu/2001/10/16/stories/05162524.htm the original] on 28 February 2002<span class="reference-accessdate">. Retrieved <span class="nowrap">20 October</span> 2016</span>.</cite></ref> {{Efn|Ghulam Muhiuddin Bute Shah in his ''Tarikh- i-Punjab'' reiterates this narrative.}} रंजीत सिंह के दरबारी इतिहासकार सोहन लाल सूरी अपना मजिस्ट्रेटी ''उमदत उत तवारीख'' (लगभग 1805) में हुसैन खान के तर्क के बिस्तार से दोहरावे के विकल्प चुनलें: तेग बहादुर सभ वर्ग के बिद्रोही लोग के शरण दे चुकल रहलें आ पंजाब भर में एगो बिसाल खानाबदोश सेना के कमान संभालले रहलें; एह से ओकरा के जल्दी से जल्दी नीचे गिरा दिहल गइल, कहीं ऊ निकट भविष्य में विद्रोह के घोषणा ना कर देव। <ref name="sc2001" />
== विरासत आ स्मारक ==
[[चित्र:Gurdwara_Rakabganj_Sahib,_Delhi.jpg|thumb|300x300px| [[गुरद्वारा रकाब गंज साहब]], दिल्ली]]
गुरु हर गोविंद गुरु तेग बहादुर के बाबूजी रहले। इनके मूल नाँव ''त्याग मल'' ( {{Lang-pa|ਤਿਆਗ ਮਲ}} ) बाकिर बाद में मुगल सेना के खिलाफ लड़ाई में उनकर बहादुरी आ बहादुरी के बाद एकर नाँव ''तेग बहादुर'' रखल गइल। उहाँ के आनंदपुर साहब शहर के निर्माण कइले आ कश्मीरी पंडित लोग के एगो गुट के बचावे के जिम्मेदारी उनकरा पर रहे, जेकरा के मुगल लोग प्रताड़ित करत रहे।
मुगल सम्राट औरंगजेब द्वारा तेग बहादुर के मृत्युदंड के बाद उनके आ उनके साथी लोग के याद में कई गो सिख मंदिर बनावल गइल। दिल्ली के चांदनी चौक में [[गुरुद्वारा शीशगंज साहिब|गुरद्वारा सिस गंज साहब]] जहवाँ सिर काटल गइल रहे, ओहिजा बनल। <ref name="skc">SK Chatterji (1975), Sri Guru Tegh Bahadur and the Sis Ganj Gurdwara, Sikh Review, 23(264): 100–09</ref> गुरद्वारा रकाब गंज साहब, जवन भी दिल्ली में बा, तेग बहादुर के एगो शिष्य के आवास के जगह प बनल बा, जवन कि अपना मालिक के लाश के दाह संस्कार करे खाती आपन घर जरा देले रहले।
पंजाब के गुरद्वारा सिसगंज साहब ओह जगह के चिन्हित करेला जहाँ नवंबर 1675 में औरंगजेब के मुगल अधिकार के अवहेलना करत भाई जैता ( सिख संस्कार के अनुसार भाई जीवन सिंह नाम बदलल ) द्वारा ले आवल गइल शहीद गुरु तेग बहादर के सिर के इहाँ अंतिम संस्कार कइल गइल रहे। <ref>Harbans Singh (1992), "History of Gurdwara Sis Ganj Sahib", in ''Encyclopedia of Sikhism, Volume 1'', pg. 547</ref> आनंदपुर साहब के यात्रा के दौरान भाई जैता सिंह [[सोनीपत]] में दिल्ली के नजदीक एगो गांव पहुंचेले अउरी मुगल सेना भी ओ गांव पहुंच जाले। <ref>{{Cite web|url=http://dibhu.com/index.php/2019/05/06/baba-kushal-singh-ji-dahiya/|title=Archived copy|archive-url=https://web.archive.org/web/20210224171945/http://dibhu.com/index.php/2019/05/06/baba-kushal-singh-ji-dahiya/|archive-date=24 February 2021|access-date=5 January 2020}}</ref> भाई जइता गाँव के लोग से मदद के मांग करते से गाँव के लोग गुरु' सिर से भाई जैता के लुकवा दिहल। <ref>{{Cite web|url=https://hindi.pratilipi.com/read/%E0%A4%B5%E0%A5%80%E0%A4%B0-%E0%A4%AC%E0%A4%B2%E0%A4%BF%E0%A4%A6%E0%A4%BE%E0%A4%A8%E0%A5%80-%E0%A4%95%E0%A5%81%E0%A4%B6%E0%A4%BE%E0%A4%B2-%E0%A4%B8%E0%A4%BF%E0%A4%82%E0%A4%B9-%E0%A4%A6%E0%A4%B9%E0%A4%BF%E0%A4%AF%E0%A4%BE-ttraa5um49oe-4v256n71255eh9d|title=Pratilipi {{!}} Read Stories, Poems and Books|website=hindi.pratilipi.com|language=hi|archive-url=https://web.archive.org/web/20210926165700/https://hindi.pratilipi.com/read/%E0%A4%B5%E0%A5%80%E0%A4%B0-%E0%A4%AC%E0%A4%B2%E0%A4%BF%E0%A4%A6%E0%A4%BE%E0%A4%A8%E0%A5%80-%E0%A4%95%E0%A5%81%E0%A4%B6%E0%A4%BE%E0%A4%B2-%E0%A4%B8%E0%A4%BF%E0%A4%82%E0%A4%B9-%E0%A4%A6%E0%A4%B9%E0%A4%BF%E0%A4%AF%E0%A4%BE-ttraa5um49oe-4v256n71255eh9d|archive-date=26 September 2021|access-date=2020-01-05}}</ref> कुशल सिंह दहिया नाम के एगो गाँव के आदमी आगे आके गुरु के सिर के जगह आपन सिर मुगल सेना के सौंप देवे के आग्रह कइलें। <ref>{{Cite web|url=https://m.khaskhabar.com/news/news-cm-manohar-lal-unveils-statue-of-kushal-singh-dahiya-at-rai-in-sonipat-news-hindi-1-270473-KKN.html|title=कुशाल सिंह दहिया की प्रतिमा का सीएम ने किया अनावरण|date=2017-11-09|website=khas khabar|language=hi|archive-url=https://web.archive.org/web/20181127110640/https://m.khaskhabar.com/news/news-cm-manohar-lal-unveils-statue-of-kushal-singh-dahiya-at-rai-in-sonipat-news-hindi-1-270473-KKN.html|archive-date=27 November 2018|access-date=2020-01-05}}</ref> कुशल सिंह दहिया के सिर काटला के बाद गाँव के लोग सिर फेरबदल क के कुशल सिंह दहिया के सिर मुगल सेना के दे देले। <ref>{{Cite web|url=https://www.dailypioneer.com/2017/state-editions/cm-unveils-statue-of-kushal-singh-dahiya.html|title=CM unveils statue of Kushal Singh Dahiya|last=Pioneer|first=The|website=The Pioneer|language=en|archive-url=https://web.archive.org/web/20191221103657/https://www.dailypioneer.com/2017/state-editions/cm-unveils-statue-of-kushal-singh-dahiya.html|archive-date=21 December 2019|access-date=2020-01-05}}</ref>
तेग बहादुर के याद कइल जाला कि ऊ धर्म के आजादी खातिर आपन जान छोड़ दिहलन, भारत के सिख आ गैर मुसलमानन के याद दिआवत बाड़न कि ऊ लोग अपना मान्यता के पालन करे आ ओकरा के पालन करे। गुरु तेग बहादुर शहीद हो गइलन, साथी भक्त भाई माटी दास, भाई सती दास आ भाई दयाला के साथे शहीद हो गइलन। <ref name="bbcgtb">{{Cite web|url=http://www.bbc.co.uk/religion/religions/sikhism/people/teghbahadur.shtml|title=Religions – Sikhism: Guru Tegh Bahadur|publisher=[[BBC]]|archive-url=https://web.archive.org/web/20170414075330/http://www.bbc.co.uk/religion/religions/sikhism/people/teghbahadur.shtml|archive-date=14 April 2017|access-date=20 October 2016}}<cite class="citation web cs1" data-ve-ignore="true">[http://www.bbc.co.uk/religion/religions/sikhism/people/teghbahadur.shtml "Religions – Sikhism: Guru Tegh Bahadur"]. [[बीबीसी|BBC]]. [https://web.archive.org/web/20170414075330/http://www.bbc.co.uk/religion/religions/sikhism/people/teghbahadur.shtml Archived] from the original on 14 April 2017<span class="reference-accessdate">. Retrieved <span class="nowrap">20 October</span> 2016</span>.</cite></ref> 24 नवंबर, इनके शहादत के तारीख भारत के कुछ हिस्सा में सार्वजनिक छुट्टी के रूप में मनावल जाला। <ref>{{Cite web|url=http://www.dnh.nic.in/Docs/14Dec2015/HolidayNotification2016.pdf|title=Letter from Administration of Dadra and Nagar Haveli, U.T.|website=Dnh.nic.in|archive-url=https://web.archive.org/web/20160806124235/http://www.dnh.nic.in/Docs/14Dec2015/HolidayNotification2016.pdf|archive-date=6 August 2016|access-date=20 October 2016}}</ref><ref>{{Cite web|url=http://arunachalipr.gov.in/GH_Restricted.htm|title=LIST OF RESTRICTED HOLIDAYS 2016|website=Arunachalipr.gov.in|archive-url=https://web.archive.org/web/20161108090922/http://arunachalipr.gov.in/GH_Restricted.htm|archive-date=8 November 2016|access-date=20 October 2016}}</ref><ref>{{Cite web|url=http://himachal.nic.in/en-IN/holidays-2016.html|title=HP Government – Holidays – Government of Himachal Pradesh, India|date=13 June 2016|website=Himachal.nic.in|archive-url=https://web.archive.org/web/20161101172904/http://himachal.nic.in/en-IN/holidays-2016.html|archive-date=1 November 2016|access-date=20 October 2016}}</ref>
फांसी के सजा मुस्लिम शासन अउरी उत्पीड़न के खिलाफ सिख लोग के संकल्प कड़ा हो गइल। पशौरा सिंह के कहनाम बा कि, "अगर गुरु अर्जन के शहादत से सिख पंथ के एक संगे ले आवे में मदद मिलल रहित त गुरु तेग बहादुर के शहादत से मानव अधिकार के रक्षा के एकर सिख पहचान के केंद्रीय बनावे में मदद मिलल"। विल्फ्रेड स्मिथ <ref name="ws1981" /> के कहनाम रहल कि "नौवाँ गुरु के जबरन बाहरी, अवैयक्तिक इस्लाम में बदले के कोसिस से शहीद के नौ बरिस के बेटा गोबिंद पर साफ-साफ अमिट छाप पड़ल जे अंत में सिख समूह के संगठित क के धीरे-धीरे बाकी जानबूझ के प्रतिक्रिया दिहलें।" एगो अलग, औपचारिक, प्रतीक पैटर्न वाला समुदाय"। एहमें खालसा पहचान के सुरुआत भइल।
== नोट ==
{{Notelist}}
== संदर्भ ==
{{Reflist}}
== बाहरी कड़ी ==
; गुरु तेग बहादुर पर पियर-रिव्यू प्रकाशन
* [https://web.archive.org/web/20151117023122/http://books.sikhbookclub.com/2013/pdfbook/Guru_Tegh_Bahadur_Divine_Poet_Saviour_And_Martyr_By_Ranbir_Singh.pdf गुरु तेग बहादुर] रणबीर सिंह (1975)
* [https://www.jstor.org/stable/606560 सातवाँ आ नौवाँ सिख गुरु लोग के श्रेय वाली गैर-कैनोनिकल रचना], जीवन सिंह देओल, जर्नल ऑफ द अमेरिकन ओरिएंटल सोसाइटी, 121(2): 193-203, (अप्रैल। – जून, 2001) के बा।
{{S-start}}
{{Succession box
| title = [[Sikh gurus|Sikh Guru]]
| years = 20 March 1665 – 24 November 1675
| before = [[Guru Har Krishan]]
| after = [[Guru Gobind Singh]]
}}
{{S-end}}
{{Authority control}}
[[श्रेणी:पंजाबी लोग]]
[[श्रेणी:पन्ना जिनहन में बिना समीक्षा भइल अनुबाद बाटे]]
[[श्रेणी:सिख धर्म]]
bukkq8rt8mgr72d0lcyw28ml2rj9hpt
कुट्टनाड
0
87746
803374
786887
2026-07-29T16:24:42Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803374
wikitext
text/x-wiki
[[चित्र:Kerala_Water_Transport_DS.jpg|thumb|कुट्टानाडु में सार्वजनिक परिवहन नाव सेवा]]
'''कुट्टानड''' ( {{Lang-ml|കുട്ടനാട്}} ) भारत के [[केरल]] राज्य में अलप्पुझा, कोट्टायम आ पठानमथिट्टा जिला के कवर करे वाला इलाका हवे जे अपना बिसाल धान के खेत आ भौगोलिक बिसेसता सभ खातिर जानल जाला। ई इलाका भारत के सभसे कम ऊँचाई वाला इलाका हवे, आ दुनिया के कुछ अइसन जगहन में से एक हवे जहाँ खेती लगभग {{Convert|4|to|10|ft|m|order=flip}} समुद्र तल से नीचे होखे ला। <ref>{{Cite news|url=https://www.ndtv.com/kerala-news/coronavirus-lockdown4-kerala-government-boat-ferries-lone-passenger-sandra-babu-to-help-her-take-exam-2238752|title=Kerala Boat Ferries Lone Passenger To Help Her Take Exam|last=Press Trust of India|date=1 June 2020|work=NDTV|access-date=17 November 2020}}</ref><ref>{{Cite web|url=http://www.hindu.com/mag/2003/07/13/stories/2003071300600200.htm|title=Thirst below sea level|last=Suchitra|first=M|date=2003-08-13|website=[[The Hindu]]|archive-url=https://web.archive.org/web/20190922101804/https://www.thehindu.com/todays-paper/tp-features/tp-sundaymagazine/thirst-below-sea-level/article28523147.ece|archive-date=2019-09-22|access-date=2020-11-17}}</ref> दक्खिन भारत के प्राचीन इतिहास में कुट्टनड ऐतिहासिक रूप से महत्वपूर्ण बा आ राज्य के प्रमुख चावल उत्पादक देश हवे। कुटनाड के किसान बायोसलाइन खेती खातिर मशहूर बाड़े। खाद्य आ कृषि संगठन (एफएओ) कुट्टानड खेती प्रणाली के वैश्विक रूप से महत्वपूर्ण कृषि धरोहर प्रणाली (जीआईएएचएस) घोषित कइले बा।
केरल के चार गो प्रमुख नदी पाम्बा, मीनाचिल, अचनकोविल आ मनीमाला एह इलाका में बहे लीं। ई पुन्नामाडा बैकवाटर में नाव दौड़ खातिर जानल जाला, मलयालम में वल्लमकल्ली के नाँव से जानल जाला।
== इतिहास ==
भरपूर धान के खेत आ झील वाला एह इलाका में चावल, सब्जी & फल, मछरी, डेयरी उत्पाद आ मुर्गी के मांस के उत्पादन होला जवन स्थानीय रूप से त्रावणकोर किंगडम के तहत आलाप्पुझा, चंगनाचेरी आ कोट्टायम गाँव के बाजार में बेचल जात रहे। जब केरल राज्य के अस्तित्व आइल तब निचला कुट्टानड क्षेत्र के परिदृश्य के आधार पर आलाप्पुझा आ कोट्टायम जिला में, ऊपरी कुट्टानड के आलाप्पुझा आ पठानमथिट्टा जिला में आ उत्तरी कुट्टानड के कोट्टायम में सामिल कइल गइल। एह भूमि के पहिला दर्ज इतिहास [[संगम काल]] के साहित्य से मिलल बा। संगम युग के ग्रंथन के अनुसार '''उथियाँ चेरलटन''' ( '''पेरम चोररू उडियान चेरलथन''', अथन '''I''' या '''उडियांजेरल''' ) प्राचीन [[केरल]] में [[संगम काल]] के पहिला दर्ज चेरा राजवंश के शासक हवें। {{Sfn|Singh|2008|p=384}} इनके राजधानी कुट्टानाड (मध्य [[केरल]] ) के कुझुमूर नाँव के जगह पर रहल आ ऊ राज्य के बिस्तार अपना मूल माटी से उत्तर आ पूरब ओर कइलें। इनके जीवनकाल मोटा-मोटी ईसा पूर्व पहिली सदी से दूसरी सदी ईसवी के बीच के निर्धारित कइल गइल बा। उनकर रानी वेलियान वेनमन के बेटी वेलियान नलिनी रहली।{{प्रमाण देईं|date=February 2022}}
उथियाँ चेरलथन चोल शासक करिकला चोला के समकालीन रहलें। हाथी कोर सेना आ घुड़सवार सेना खातिर उनकर तारीफ होला। वर्तमान समय के चंगनाचेरी कुट्टानड के अंत चेर राजवंश के राजा उथियान चेरलथन के राजधानी रहे। इनके वंशज सेनकुट्टुवन (चेनकुटतुवन के मतलब [[मलयालम]] में "लुभावन कुट्टुवन" ) रहल आ इनके नाँव वर्तमान समय ले चेंगनाचेरी आ चेंगना शहर सभ द्वारा ले जाइल जाला)। कुट्टूवा जनजाति के मूल स्थान कुट्टनडु के नाम से जानल जाए लागल। संगम साहित्य के मोताबिक कारीकला चोला के खिलाफ वेन्नी के लड़ाई में उथियाँ चेरलाथन हार गइल रहे आ राजधानी जरा दिहल गइल रहे।{{प्रमाण देईं|date=February 2022}}
हाल के अतीत तक 'करिनिलाम'(काला धान के खेत) के नाम से कहल जाए वाला धान के खेत से करिया लकड़ी के खनन होखत रहे। कुट्टानड में अधिकतर जगह के नाँव 'कारी' (मतलब जरावल अवशेष भा कोयला) से खतम होला। कुछ परिचित जगह के नाँव बा रमणकारी, पुथुक्करी, अमीचकारी, ऊरुक्करी, मिथराकारी, मम्पुझाकरी, कैनाकारी, कंदनाकारी, थायमकारी, चठेंकरी, चथुरठियाकारी आ चेन्नमकरी ।{{प्रमाण देईं|date=February 2022}}
== डिवीजन ==
कुट्टानाड क्षेत्र के तीन गो भाग में बाँटल गइल बा:
* लोअर कुटनाड
* ऊपरी कुटनाड
* उत्तर कुटनाड
निचला कुट्टानाडु में अम्बलपुझा, नेदुमुडी, कुट्टानाडु ( एदाथुआ, थलावाडी, किदंगरा आ मुत्तर के छोड़ के), आ आलाप्पुझा [[अलाप्पुझा जिला|जिला]] के कार्तिकपल्ली तालुका के उत्तरी आधा हिस्सा के तालुका सामिल बाड़ें।
ऊपरी कुट्टानड में कार्तिकपल्ली तालुका में वीयापुरम आ पल्लीप्पड, कुट्टानड तालुक में एडथुआ, थलावाडी, किदंगरा आ मुत्तर सामिल बाड़ें; मावेलिकरा तालुका में चेन्निथला आ थ्रिप्पेरुमथुरा गाँव; आलाप्पुझा [[अलाप्पुझा जिला|जिला]] के चेंगनूर तालुका के मन्नार, कुराटिसेरी, बुधनूर, एननक्कड गाँव ; आ पठानमथिट्टा जिला के परुमाला, कदपरा, निरानम, पुलिकेझू, नेदुमपुरम, चठेंकरी, पेरिंगारा, आ कवुम्भागम गाँव में [[पथानामथिट्टा जिला|बा]]।
उत्तरी कुट्टनड में वैकोम तालुका, कोट्टायम तालुका के पच्छिमी हिस्सा, आ [[कोट्टायम जिला]] के चंगनाचेरी तालुका के पच्छिमी हिस्सा सामिल बा।
== बैकवाटर धान के खेती (कायल खेती) ==
[[चित्र:Kerala_Kuttanad2.jpg|thumb|कुट्टानड में धान के खेत]]
कुट्टानाडु के प्रमुख पेशा खेती बा, जवना में धान सबसे महत्वपूर्ण कृषि उत्पाद बा। एह गतिविधि से एह इलाका के "केरल के चावल के कटोरा" के नाँव दिहल गइल हवे। [[वेम्बनाड झील]] के लगे बड़हन खेती के इलाका झील से वापस ले लिहल गइल। कुट्टानड में धान के खेती के इतिहास सदियन से पहिले के पता लगावल जा सकेला। धान के खेती के बिकास तकनीकी उन्नति आ नियामक ढाँचा में बदलाव के साथ सहसंबंधित रहल जे 19वीं आ 20वीं सदी के दौरान मौजूद रहल। पहिले के समय में मुख्य रूप से [[वेम्बनाड झील]] के उथला हिस्सा से या पम्बा नदी के परिधि से रिकलेमेशन कइल जात रहे। एह रिक्लेमेशन से धान के खेत के छोट इलाका के गठन भइल जवना के ''पदशेखराम'' कहल जाला। खेत से पानी से भराव के निकाले के काम ''चक्रम'' नाम के पानी के पहिया के इस्तेमाल से मैन्युअल रूप से कइल जात रहे। धीरे-धीरे पानी से निकाले खातिर इस्तेमाल होखे वाला मैनुअल तरीका के जगह भाप इंजन ले लिहलस।{{प्रमाण देईं|date=February 2022}}
पहिले के दिनन में कुट्टानड में लूटपाट होखत रहे, जवना प त्रावणकोर महाराजा मूलम तिरुनाल के ओर से रोक लगावल गइल रहे।
झील से कयाल जमीन सभ के रिक्लेमेशन में तीन गो अलग-अलग स्टेज सभ के पहिचान कइल जा सके ला। पहिला चरण के निजी उद्यमी बिना सरकार के कवनो आर्थिक सहयोग के पूरा कइले। 1865 में ट्रेवेनकोर किंगडम द्वारा कइल गइल पैटम घोषणा, 1865 से 1888 के बीच रिकलेमेसन गतिविधि सभ के बहुत ढेर बढ़ावा दिहलस। एह दौरान पोल्डर सभ के पानी मुक्त करे के काम मैन्युअल रूप से ''चक्रम'' के इस्तेमाल से कइल गइल, जेकरा से बड़ पैमाना पर रिकलेमेशन ना कइल जा पावत रहल। एह दौरान मात्र लगभग 250 हेक्टेयर जमीन के पुनर्प्राप्ति भइल। एह दौरान वेनाडु कयाल आ मदथिल कायल के रिकलेम कइल गइल आ वेम्बनाड झील से रिक्लेम कइल गइल पहिला "कायल नीलाम" मानल जाला। <ref>{{Cite news|url=http://www.fao.org/fileadmin/templates/giahs_assets/Information_Resources_Annexes/Japan_Forum/Kuttanad_Below_Sea_Level_Farming_System.pdf|title=The Kuttanadu Below Sea Level Farming System, India|last=M.S. Swaminathan, FRS|date=May 2013|work=FAO Website|access-date=8 Oct 2013|archive-url=https://web.archive.org/web/20150611175117/http://www.fao.org/fileadmin/templates/giahs_assets/Information_Resources_Annexes/Japan_Forum/Kuttanad_Below_Sea_Level_Farming_System.pdf|archive-date=11 June 2015|pages=8}}</ref> कयाल खेती में ई अग्रणी रिक्लेमेशन गतिविधि कुट्टानाडु के कैनाडी गाँव के दुनु भाई मथाई लुका पलिथानम आ औसेफ लुका पलिथानम कइले रहले।<ref>{{Cite news|url=http://www.kerala.gov.in/docs/publication/2013/kc/september_13/16.pdf|title=Kuttanad; Our Heritage Our wealth|last=G S Unnikrishanan Nair|date=Sep 2013|work=KERALA CALLING|access-date=26 Sep 2013|archive-url=https://web.archive.org/web/20130927092458/http://www.kerala.gov.in/docs/publication/2013/kc/september_13/16.pdf|archive-date=2013-09-27|pages=16–20}}</ref>
दूसरा चरण 1888 के दौरान शुरू भइल। एह दौरान एगो रिक्लेमेशन चलायल एरावी केसवा पनिकर कइले रहले। उ चेन्नमकरी नदी के मुहाना से वेम्बनाड कयाल के रिक्लेम करे के फैसला कइलें, काहेंकी इ पीछे के पानी से जुड़ जाले। पुनर्प्राप्त कयाल के ' अतुमुटु कायल' के नाम से जानल जात रहे। एही साल एगो अउरी बड़हन रिक्लेमेशन रहे सेमिनरी कायल जवन कोट्टायम रूढ़िवादी सेमिनरी कइले रहे।{{प्रमाण देईं|date=February 2022}}
पानी के निकासी खातिर मिट्टी के तेल के इंजन के शुरूआत के परिणामस्वरूप झील के बिसाल इलाका सभ के खेती खातिर रिक्लेमेशन भइल। एह से किसान लोग झील के गहिराह हिस्सा में उतरे पर विचार कइ सकल। 1898 से 1903 के बीच के समय में, रिक्लेमेशन गतिविधि के नेतृत्व पल्लीथनम लुका मथाई (उर्फ पल्लीथानाथु मथैचेन) कइलें जे चेरुकारा कायल आ पल्लीथनम मूवायराम कायल के रिक्लेम कइलें। बाकिर दुसरका चरण (1890 से 1903) के रिक्लेमेशन गतिविधि ठप्प हो गइल काहे कि 1903 में मद्रास सरकार द्वारा कयाल रिकलेमेशन पर रोक लगावल गइल रहे। एह दौरान चेरुकाली कायल, राजापुरम कायल, आरुपंकू कायल, पंथर्दू पंकू कायल आ माथी कायल अउरी प्रमुख रिक्लेमेशन रहलें।{{प्रमाण देईं|date=February 2022}}
1912 में मद्रास सरकार त्रावणकोर सरकार के ओर से तीन चरण में अउरी रिक्लेमेशन खातिर एगो प्रस्ताव के मंजूरी दे दिहलस। एह योजना के तहत कयल जमीन के ब्लॉक में पुनर्प्राप्ति खातिर अधिसूचित कइल गइल रहे जवना के नाम हर ब्लॉक के अंग्रेजी वर्णमाला के अक्षर से रखल गइल रहे| कुल 19,500 एकड़ कयाल जमीन में से 12,000 एकड़ जमीन के 1913 से 1920 के बीच रिकलेम कइल गइल। 1913 में प्रतिबंध हटावे के बाद पल्लीथनम लुका मथाई कुट्टानाडु के कुछ अउरी प्रमुख परिवारन के साथे मिल के कुल क्षेत्रफल 2,400 एकड़ के ई-ब्लॉक कायल के रिक्लेम कइल गइल। ई कुट्टानाडु के सबसे बड़ कायल निलाम ह। एह उद्यम में सीजे कुरियन, एक्स एमएलसी आ श्री जॉन इल्लिकालम उनुकर मुख्य साझेदार रहलें। 1914 से 1920 के बीच के रिक्लेमेशन, जेकरा के नया रिक्लेमेशन के नाँव से जानल जाला, तीन काल में भइल। पहिला अवधि में 6300 एकड़ के ब्लॉक ए से जी के रिकलेम कइल गइल। सी ब्लॉक, डी ब्लॉक (अत्तुमुखम आरायीराम (अतुमुतु कयाल), थेक्के आरायीराम आ वडक्के आरयिम) आ ई ब्लॉक (एरुपतिनालायिराम कायल) एफ ब्लॉक (जज के आरयिराम कायल) आ जी ब्लॉक (कोचु कायल) एह दौरान पुनर्प्राप्त प्रमुख कायल नीलम हवें।{{प्रमाण देईं|date=February 2022}}
नया रिकलेमेशन के दूसरा दौर में 3600 एकड़ के क्षेत्रफल में फैलल ब्लॉक एच से एन के पल्लीथनम लुका मथाई, कुन्नुम्पुराथु कुरिएन, वाचपरम्पिल माथेन, पझायापरम्पिल चको, कुन्नाथुसेरिल पेयस, एट्टूपरैल जेवियर आ पट्टासेरी पीपी मथाई के नेतृत्व में रिकलेम कइल गइल। नया रिकलेमेशन के तीसरा अवधि के दौरान श्री मूलम लोकप्रिय सभा के तत्कालीन सदस्य पल्लीथनम लुका मथाई के नेतृत्व में आठ परिवार के संयुक्त प्रयास से 1,400 एकड़ में फैलल आर ब्लॉक कायल के रिक्लेम कइल गइल। रिक्लेमेशन में उनकर साझीदारन में वाचपरम्पिल माथेन, पझायापरम्पिल चक्को, एट्टूपरायल जेवियर, पट्टासेरी पीपी मथाई, करिककुझी पोन्नाडा वक्कल मथुल्ला मप्पिला (ई एंड एफ ब्लॉक), मेलेडोम, परुथिकल आ कंडाकुडी शामिल रहले. एक बेर त्रावणकोर के राजा एरुपथिनालायिराम कायल के दौरा कइलन त ऊ खुश हो गइलन आ कारिक्कुझी मथुल्ला मप्पिला से कहले कि उनका सम्मान खातिर पोन्नाडा खरीदे के चाहत रहे। चूँकि ऊ पोन्नाडा ना खरीदले रहलें एहसे ऊ "पोन्नाडा वक्कल" (शब्द से पोन्नाडा) पेश कइलन। एकरा बाद उनकर घर पोन्नाडवाक्कल के नाम से जानल गइल।{{प्रमाण देईं|date=February 2022}}
1920 से 1940 तक चावल के दाम में भारी गिरावट के चलते रिक्लेमेशन के गतिविधि ठप हो गइल।
[[चित्र:Kuttanad_beauty.jpg|alt=Kuttanad beauty|thumb| '''कुट्टानड के सुंदरता''']]
[[चित्र:Rice_fields_of_Kuttanad.jpg|left|thumb| धान के खेत में पारंपरिक पानी के टरबाइन का उपयोग कर रहल किसान]]
1920 से 1940 के बीच चावल के दाम में भारी गिरावट के चलते रिकलेमेसन गतिविधि में धीमापन आइल, लेकिन 1940 के दशक के शुरुआत में एकरा के फेर से गति मिलल। एह दौरान कृषि उत्पादन बढ़ावे खातिर एगो सरकार "अधिक खाद्य उगाईं" अभियान शुरू कइलस आ प्रोत्साहन के प्रावधान से नया पुनर्वास के प्रोत्साहित कइल गइल। इलेक्ट्रिक मोटर के आगमन से पहिले के समय के तुलना में रिकलेमेशन अपेक्षाकृत आसान, सस्ता आ कम जोखिम वाला हो गइल। रिक्लेमेशन के अंतिम खंड यानी क्यू, एस आ टी ब्लॉक एह दौरान जोसेफ मुरिकन (मुरिकेन आउटहचन) आ उनकर मेहरारू के परिवार पुथनप्पुरा पंचरा (वेलियानाडु) द्वारा बनावल गइल।
जइसे-जइसे ए इलाका में खेती बढ़ल, किसान धान के खेती खाती साल में होखेवाला दु चक्र से अपना के बाध्य महसूस कइले। जवना के कारण कुट्टानाडु में पीये लायक पानी के सीमित उपलब्धता बा। मानसून के मौसम में पहाड़ से पानी नदी से होके समुंद्र में बह के कुट्टानाडु में पीये लायक पानी ले आवेला। बाकिर गर्मी के समय एह इलाका के कम स्तर के कारण समुंद्र के पानी कुट्टानाडु में प्रवेश करे ला जेवना से पानी के लवणता काफी बढ़ जाला आ ई पीये लायक ना रह जाला।{{प्रमाण देईं|date=February 2022}}
साथ ही कुट्टानाड के एफएओ द्वारा ग्लोबलली इम्पोर्टेंट एग्रीकल्चरल हेरिटेज सिस्टम (GIAHS) के रूप में मान्यता दिहल गइल बा।
== थोट्टप्पली स्पिलवे ==
थोट्टप्पली स्पिलवे परियोजना के डिजाइन कुट्टानड में बाढ़ के स्थिति के स्थायी समाधान के रूप में बनावल गइल रहे। एह कार्यक्रम के परिकल्पना अइसन कइल गइल कि पम्बा, मणिमालायर आ अचनकोविल से बाढ़ के पानी वेम्बनाद झील में चहुँपे से पहिले समुद्र में मोड़ दिहल गइल। ई स्पिलवे 1955 में चालू भइल <ref>{{Cite news|url=https://www.onmanorama.com/news/kerala/2020/08/01/thottappally-spillway-opened-to-prevent-flooding-kuttanad.html|title=Thottappally spillway opened to prevent flooding in Kuttanad|work=OnManorama}}</ref>
1968 में भारत सरकार प्रस्ताव रखलस कि नदी के ओह पार एगो बंधा बनावल जाय ताकि गर्मी के समय में समुंद्र के पानी के कुट्टानड के भीतर ना आवे दिहल जा सके, जेह से किसान लोग के हर साल एक ठो अतिरिक्त फसल के खेती हो सके। एह परियोजना के योजना तीन चरण में बनावल गइल रहे, दक्षिण ओर, उत्तर ओर आ एगो अउरी चरण में दुनो खंड के जोड़े खातिर। परियोजना में देरी हो गइल अउरी जब तक पहिला दु चरण पूरा हो गइल तब तक ए परियोजना खाती आवंटित पूरा पइसा खतम हो गइल अउरी अंतिम चरण अनिश्चितता में लटक गइल। जवन किसान परियोजना पूरा होखला के बाद बहुत आर्थिक लाभ के उम्मीद करत रहले, उ लोग मामला के अपना हाथ में लेवे के फैसला कइलें अउरी 1972 में एक रात किसान के एगो बड़ समूह उत्तर अउरी दक्षिण ओर के बीच के खाई के माटी से भर देलस। आजु ले बंधा के दुनों खंड के बीच के माटी के तटबंध बनल बा। एकरे साथ दिसंबर–जून के दौरान जब खारा पानी घुसे ला तब शटर सभ के रेगुलेटर बंद कइल संभव हो गइल, आ फिर मानसून के दौरान खोलल जा सके ला। एक बेर ठनीरमुक्कम बंड आ स्पिलवे संचालित हो गइल त कुट्टानड में दू गो फसल संभव हो गइल।
भले ही बंड से किसान के जीवन के गुणवत्ता में सुधार भइल बा, लेकिन ए बंड के चलते पर्यावरण के गंभीर समस्या पैदा होखे के आरोप बा। बैकवाटर जवन मछरी के भरमार रहे आ एह इलाका के लोग के मुख्य भोजन के हिस्सा रहे ओकरा प्रजनन खातिर थोड़ बहुत नमकीन पानी के जरूरत होला। एह बंधा के चलते एह इलाका में मछरी के किसिम खराब हो गइल बा आ मछुआरा लोग 2005 ले एह बंधा के विरोध कइले बा। बंधा से समुंद्र के बैकवाटर के साथे सामंजस्य भी बाधित हो गइल बा आ पानी के खरपतवार के सर्वव्यापी होखे जइसन समस्या पैदा भइल बा जवन बंधा से पहिले ना देखल गइल रहे। बंधा के निर्माण से पहिले खारा पानी में बैकवाटर के साफ करे के प्रवृत्ति रहे, लेकिन अब अइसन ना होखेला, जवना के चलते बैकवाटर अउरी पास के जमीन के प्रदूषण होखेला।
== उल्लेखनीय मूल निवासी आ निवासी लोग ==
{{Columns-list|<!---♦♦♦♦ Do not add anyone to this list unless they have their own Wikipedia article ♦♦♦♦--->
* [[Kavalam Sreekumar]] - Singer
* [[Pallithanam Luca Matthai]], former member of Sree Moolam Popular Assembly and pioneer of kayal cultivation in Kuttanad.
* [[Renji Panicker]], actor, director, writer and a journalist from Nedumudi village of Kuttanadu.
* [[K. K. Nayar]], administrator and politician.
* [[Nedumudi Venu]], Actor
* [[Ayyappa Panikkar|K. Ayyappa Panikkar]]
* [[Kavalam Narayana Panikkar]]
* [[Thakazhi Sivasankara Pillai]], famous novelist, [[Jnanpith]] scholar
* [[Vinayan]], film director.
* [[K. M. Panikkar]], scholar, journalist, historian, administrator, diplomat
* [[K P Sasidharan]] Professor, Malayalam writer, Soviet Land, Sangeeth Nataka academy award winner
* [[Baselios Geevarghese II]], Kallasseri Bava, Kurichy, 3rd Catholicos of Orthodox Church
* [[Kuriakose Elias Chavara]], Indian saint
* [[John Abraham (director)|John Abraham]], film director
* [[Kunchacko Boban]], film actor and director
* [[Saji Thomas]], International Sportsman, Arjuna Awardee
* [[I. C. Chacko, Illiparambil|I C Chacko]], physicist and geologist, state geologist of Travancore State from 1906 to 1921
* [[Thomas Chandy]], MLA, politician
* [[Ramesh Chennithala]], former Minister, UDF political leader
* [[Guru Gopinath]], Indian classical dancer
* [[K. C. Joseph (Kuttanadu politician)|K. C. Joseph]], former MLA,
* [[Kunchacko]], film producer and director
* [[Mar Thomas Kurialacherry]], former archbishop
* [[Oommen Mathew]], Former MLA, politician
* [[Venu Nagavally]], film actor and director
* [[Mankompu Sivasankara Pillai]] - [[Kathakali]] artist of the classical dance-drama's southern style
* [[Ponkunnam Varkey]], novelist
* [[Sebastian Xavier]], former Indian Olympic swimmer from Kerala|colwidth=22em}}
== राजनीति ==
कुट्टानड विधानसभा क्षेत्र आलाप्पुझा के हिस्सा रहे। साल 2008 में [[लोकसभा]] परिसीमन के बाद अब इ मावेलिकारा क्षेत्र के बा। <ref>{{Cite web|url=http://archive.eci.gov.in/se2001/background/S11/KL_Dist_PC_AC.pdf|title=Assembly Constituencies — Corresponding Districts and Parliamentary Constituencies|website=Kerala|publisher=Election Commission of India|archive-url=https://web.archive.org/web/20090304011026/http://archive.eci.gov.in/se2001/background/S11/KL_Dist_PC_AC.pdf|archive-date=2009-03-04|access-date=2008-10-20}}</ref>
== इहो देखल जाव ==
* [[अलाप्पुझा|अलप्पुझा]]
== संदर्भ ==
{{Reflist}}
== आगे पढ़ल जा सकेला ==
* ''[http://www.countercurrents.org/en-suchitra150703.htm समुद्र तल से नीचे प्यास] {{Webarchive|url=https://web.archive.org/web/20050217031926/http://countercurrents.org/en-suchitra150703.htm |date=2005-02-17 }}'', एम सुचित्र, ''[[दि हिंदू|द हिंदू]]'', 15 जुलाई 2003
* केटी राममोहन के ह। ''चावल के किस्से: कुट्टानड, दक्षिण पश्चिम भारत'' के बा। विकास अध्ययन केंद्र, तिरुवनंतपुरम, 2006 में प्रकाशित भइल।{{ISBN|81-86353-03-8}}[[ISBN (identifier)|आईएसबीएन के बा]] [[Special:BookSources/81-86353-03-8|81-86353-03-8 पर संपर्क कइल जा सकेला]]
* केटी राममोहन. 2008. हेलेन सेलिन (संपादक) में 'दक्षिण भारत में धान के खेत के पुनर्प्राप्ति'। ''गैर-पश्चिमी संस्कृतियन में विज्ञान, प्रौद्योगिकी, आ चिकित्सा के इतिहास के विश्वकोश।'' खंड 1. 2. । बर्लिन, हाइडलबर्ग आ न्यूयॉर्क: स्प्रिंगर-वेर्लाग।{{ISBN|978-1-4020-4559-2}}[[ISBN (identifier)|आईएसबीएन के बा]] [[Special:BookSources/978-1-4020-4559-2|978-1-4020-4559-2 पर संपर्क कइल जा सकेला]]
== बाहरी कड़ी ==
* [https://www.keralatourism.org/hindi/destination/kuttanad-alappuzha/59 कुट्टनाड - केरल के चावल का कटोरा], केरल टूरिज्म के वेबसाइट पर (हिंदी में)।
[[श्रेणी:Harv and Sfn no-target errors]]
[[श्रेणी:जवन निर्देशांक विकिडेटा पर मौजूद बाटे]]
[[श्रेणी:केरल के भूगोल]]
[[श्रेणी:पन्ना जिनहन में बिना समीक्षा भइल अनुबाद बाटे]]
5xt0d1qwtf7j7g8q70frnx0wze3g9ef
द्वारिकाधीश मंदिर
0
88193
803363
789040
2026-07-29T16:23:48Z
SM7Bot
7808
/* ढांचा */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803363
wikitext
text/x-wiki
{{Infobox Hindu temple|name=Dwarkadheesh Temple <br/> द्वारकाधीश मंदिर|image=Dwarkadheesh temple.jpg|alt=|caption=The temple sikhars with the entrance in front|map_type=India Gujarat|map_caption=Location in Gujarat|coordinates={{coord|22|14|16.39|N|68|58|3.22|E|type:landmark_region:IN|display=inline,title}}|country=[[India]]|state=[[Gujarat]]|locale=[[Dwarka]]|elevation_m=|website={{URL|https://www.dwarkadhish.org/}}}}
{{Char Dham}}
'''द्वारकाधीश मंदिर''', जेकरा के '''जगत मंदिर''' भी कहल जाला, [[कृष्ण]] के समर्पित एगो [[हिंदू मंदिर]] हवे, जिनके इहाँ ''द्वारकाधीश'', या 'द्वारका के राजा' नाँव से पूजल जाला। ई मंदिर [[गुजरात|भारत के गुजरात]] के द्वारका शहर में स्थित बा, जवन हिंदू तीर्थ परिक्रमा [[चारो धाम|चार धाम]] के जाए वाला जगहन में से एगो ह। 72 खंभा पर बनल एह पांच मंजिला भवन के मुख्य तीर्थ जगत मंदिर भा निज मंदिर के नाम से जानल जाला। पुरातात्विक खोज से पता चलेला कि मूल मंदिर के निर्माण सबसे पहिले 200 ईसा पूर्व में भइल रहे। 15वीं-16वीं सदी में एह मंदिर के दोबारा निर्माण आ बिस्तार कइल गइल।<ref>1988, P. N. Chopra, "Encyclopaedia of India, Volume 1", page.114</ref>
परंपरा के अनुसार मूल मंदिर के निर्माण कृष्ण के पोता वज्रनाभ द्वारा हरि-गृह (कृष्ण के आवासीय स्थान) के ऊपर बनवावल मानल जाला। मूल संरचना के महमूद बेगडा द्वारा 1472 में नष्ट कर दिहल गइल, आ एकरे बाद 15वीं-16वीं सदी में मारु-गुर्जर शैली में बनवावल गइल।
ई मंदिर भारत में [[हिंदू|हिन्दू लोग]] द्वारा पवित्र मानल जाए वाला ''चार धाम'' तीर्थ यात्रा के हिस्सा बन गइल। [[आदि शंकर|आदि शंकराचार्य]], 8वीं सदी के हिन्दू धर्मशास्त्री आ दार्शनिक, एह तीर्थ के दौरा कइले। बाकी तीन में रामेश्वरम, बद्रीनाथ अउरी [[पुरी]] शामिल बा। आजुवो मंदिर के भीतर एगो स्मारक उनुका यात्रा के समर्पित बा। द्वारकाधीश उपमहादीप के [[विष्णु]] के 98वां दिव्य देसम हवे, जेकर महिमा ''दिव्य प्रबंध'' पवित्र ग्रंथन में बा।{{Sfn|Bandyopadhyay|2014|p=71}} मंदिर औसत समुद्र तल से ऊपर {{Convert|12.19|m}} के ऊँचाई पर बा। एकर मुँह पच्छिम के ओर बा। मंदिर के लेआउट में एगो ''गर्भगृह ( निजमंदिर'' या ''हरिग्रह'' ) आ एगो अंतराल (एगो एंटीचेम्बर) बाटे।{{Sfn|Paramāra|1996|p=87}} हालाँकि, मौजूदा मंदिर के तारीख 16वीं सदी के बतावल जाला।
== दंतकथा ==
हिन्दू किंवदंती के अनुसार द्वारका के निर्माण एगो जमीन के अइसन टुकड़ा पर कृष्ण द्वारा कइल गइल रहे जेकरा के समुद्र से वापस हासिल कइल गइल रहे। दुर्वास ऋषि एक बेर कृष्ण आ उनकर पत्नी रुक्मिणी के घरे गइल रहले। ऋषि के इच्छा रहे कि ई जोड़ी उनका के अपना महल में ले जास। ई जोड़ी सहजता से मान गइल आ ऋषि के साथे अपना महल के ओर चलल शुरू कर दिहलस। कुछ दूरी के बाद रुक्मिणी थक गइली त उ कृष्ण से पानी के निहोरा कइली। कृष्ण एगो पौराणिक छेद खोदले जवन [[गंगा नदी|गंगा]] नदी के ओह जगह पर ले आइल। दुर्वास ऋषि खिसिया गइलन आ रुक्मिणी के ओह जगह पर रहे के सराप दिहलन। जवना मंदिर में रुक्मिणी के तीर्थ मिलेला, उहे मंदिर मानल जाता जहां उ ओह समय खड़ा रहली।
== इतिहास ==
[[चित्र:Dwarakadheesh_temple,_Dwaraka.jpg|left|thumb|200x200px| मंदिर के मुख्य प्रवेश द्वार तक जाए वाली सीढ़ी]]
[[गुजरात]] के द्वारका शहर के इतिहास सदियन से चलल आ रहल बा, आ [[महाभारत]] महाकाव्य में एकर जिकिर द्वारका राज्य के रूप में कइल गइल बा। गोमती नदी के तीरे स्थित एह शहर के किंवदंती में [[कृष्ण]] के राजधानी के रूप में बतावल गइल बा। लिखाई वाला पत्थर के ब्लॉक, पत्थर सभ के सजावट के तरीका से पता चले ला कि डवेल के इस्तेमाल भइल बा आ एह जगह पर मिलल लंगर सभ के जांच से पता चले ला कि बंदरगाह के जगह खाली ऐतिहासिक समय के हवे, पानी के नीचे के कुछ ढाँचा के समय देर मध्यकालीन समय के बा। तटीय कटाव शायद ओह चीज के नाश के कारण रहे जवन एगो प्राचीन बंदरगाह रहे। <ref>{{Cite journal|last=Gaur|first=A.S.|last2=Sundaresh and Sila Tripati|date=2004|title=An ancient harbour at Dwarka: Study based on the recent underwater explorations|journal=Current Science|volume=86|issue=9}}</ref>
हिन्दू लोग के मानना बा कि मूल मंदिर के निर्माण कृष्ण के परपोता वज्रनाभ द्वारा कृष्ण के आवासीय महल के ऊपर कइल गइल। एकरा के सुल्तान महमूद बेगड़ा द्वारा 1472 में नष्ट कर दिहल गइल।
चौलुक्य शैली में वर्तमान मंदिर के निर्माण 15-16वीं सदी में भइल रहे। मंदिर के क्षेत्रफल 27-मीटर बाई 21-मीटर बा आ पूरब-पच्छिम लंबाई 29-मीटर आ उत्तर-दक्खिन चौड़ाई 23 मीटर बा। मंदिर के सबसे ऊँच चोटी 51.8 मीटर ऊँच बा।
== धार्मिक महत्व ==
[[चित्र:Dwarkadhish_Temple_20.jpg|thumb|गोमती नदी के पास द्वारकाधीश मंदिर, द्वारका]]
चूँकि ई स्थल प्राचीन नगरी द्वारका आ [[महाभारत]] युग के कृष्ण से जुड़ल बा एहसे [[हिंदू|हिन्दू लोग]] खातिर ई एगो महत्वपूर्ण तीर्थस्थल बा। ई ''"कृष्ण"'' सर्किट से संबंधित 3 गो मुख्य तीर्थस्थल में से एगो हवे, जवना में [[हरियाणा]] राज्य ''के कुरुक्षेत्र के 48 कोस परिक्रमा'', [[उत्तर प्रदेश]] राज्य के [[मथुरा]] में ब्रज परिकर्मा आ [[गुजरात]] राज्य के द्वारकाधीश मंदिर में ''द्वारका परिक्रमा'' (द्वारकादीश यात्रा) बा।
मंदिर के ऊपर के झंडा पर सुरुज आ चंद्रमा के देखावल गइल बा जेवना से ई बतावल जाला कि जबले सूरज आ चंद्रमा के पृथिवी पर मौजूद ना होखे तबले कृष्ण उहाँ मौजूद रहीहें। <ref name="indianmirror1">{{Cite web|url=http://www.indianmirror.com/temples/dwarkadish-temple.html|title=Dwarkadish Temple, Dwarkadish Temple Dwarka, Dwarkadish Temple in India|publisher=Indianmirror.com|access-date=4 March 2014}}</ref> दिन में पांच बेर तक झंडा बदलल जाला, लेकिन चिन्ह उहे रहेला। मंदिर के पांच मंजिला संरचना बा जवन बहत्तर खंभा प बनल बा। मंदिर के शिखर 78.3 मीटर ऊँच बा। <ref name="indianmirror1" /> मंदिर चूना पत्थर से बनल बा जवन अभी भी नीमन हालत में बा। मंदिर में जटिल मूर्तिकला के डिटेल बनावल गइल बा।
मंदिर में दू गो प्रवेश द्वार बा। मुख्य प्रवेश द्वार (उत्तर प्रवेश द्वार) के "मोक्ष द्वार" (मोक्ष के द्वार) कहल जाला। एह प्रवेश द्वार से मुख्य बाजार में पहुँचल जाला। दक्खिन के प्रवेश द्वार के "स्वर्ग द्वार" (स्वर्ग के द्वार) कहल जाला। एह दुआर के बाहर 56 सीढ़ी बा जवन गोमती नदी के ओर जाला। <ref>[[Dwarkadhish Temple#Chakravarti|Chakravarti 1994]], p. 140</ref> हालांकि एकर उत्पत्ति के बारे में साफ-साफ जानकारी नइखे, लेकिन पूरा भारत में हिंदू मठ संस्थान के निर्माण करे वाला संकराचार्य द्वारा स्थापित हिंदू धर्म के अद्वैत स्कूल चार धाम के उत्पत्ति के श्रेय द्रष्टा के बतावेला। <ref name="Mittal" /> चारो मठ भारत के चारो कोना में बा आ इनहन के अनुचर मंदिर उत्तर में बद्रीनाथ में बद्रीनाथ मंदिर, पूरब में पुरी में [[जगन्नाथ मंदिर, पुरी|जगन्नाथ मंदिर]], पश्चिम में द्वारका में द्वारकाधीश मंदिर आ दक्खिन में रामेश्वरम में रामनाथस्वामी मंदिर बाड़ें। भले वैचारिक रूप से मंदिर सभ हिंदू धर्म के संप्रदाय, यानी [[शैव मत|शैव]] आ वैष्णव धर्म के बीच बँटल बाड़ें, चार धाम के तीर्थयात्रा एगो अखिल हिन्दू मामला हवे। <ref name="Brockman">[[Dwarkadhish Temple#Brockman|Brockman 2011]], pp. 94-96</ref> हिमालय में चार गो निवासस्थान बाड़ें जिनहन के छोट चार धाम ( ''छोटा'' मतलब छोट) कहल जाला: बद्रीनाथ, केदारनाथ, [[गंगोत्री]] आ यमुनोत्री - ई सभ हिमालय के तलहटी पहाड़ी पर पड़े लें। मूल चार धाम सभ के बिभेद करे खातिर 20वीं सदी के बीच में ''छोटा'' नाँव जोड़ल गइल। आधुनिक काल में जइसे-जइसे एह जगहन पर तीर्थयात्री लोग के संख्या बढ़ल, एकरा के हिमालयन चार धाम कहल जाला। भारत के चार गो कार्डिनल प्वाइंट के पार के यात्रा के हिन्दू लोग पवित्र मानेला जे लोग अपना जीवन में एक बेर एह मंदिरन के दौरा करे के चाहत राखेला।<ref name="Gwynne">[[Dwarkadhish Temple#Gwynne|Gwynne 2008]], Section on Char Dham</ref> परंपरागत रूप से यात्रा के सुरुआत पुरी से पूरबी छोर से होला, घड़ी के दिशा में अइसन तरीका से आगे बढ़े ला जेकर पालन आमतौर पर हिंदू मंदिर सभ में परिक्रमा खातिर कइल जाला।<ref name="Gwynne" /> मंदिर 6.00 बजे से खुलल बा बजे से 1.00 बजे तक के बा शाम आ 5.00 बजे के बा रात के साढ़े नौ बजे तक साँझ के बा. [[जन्माष्टिमी|कृष्ण जन्माष्टमी]] पर्व भा गोकुलाष्टमी, [[कृष्ण]] के जनमदिन के मनावे के सुरुआत वल्लभ (1473-1531) कइले रहलें।
एगो किंवदंती के अनुसार मीरा बाई, प्रसिद्ध राजपूत राजकुमारी जे कवि-संत आ कृष्ण के कट्टर भक्त भी रहली, एह मंदिर में देवता में विलय हो गइली। {{Sfn|Desai|2007|p=285}} ई भारत के सात गो पवित्र शहर सप्त पुरी में से एगो हवे। {{Sfn|Bansal|2008|p=20-23}}
ई मंदिर द्वारका पीठ के भी स्थान हवे जे [[आदि शंकर|आदि शंकराचार्य]] (686-717) द्वारा स्थापित चार गो पीठ (धार्मिक केंद्र) सभ में से एक हवे जे देश में [[हिंदू]] धार्मिक बिस्वास सभ के एकीकरण के सुरुआत कइलें। ई चार मंजिला संरचना ह जवन देश के अलग-अलग हिस्सा में शंकराचार्य द्वारा स्थापित चार पीठ के प्रतिनिधित्व करेला। इहाँ के देवालन पर शंकराचार्य के जीवन इतिहास के चित्रण करे वाला चित्र बा जबकि गुंबद में अलग-अलग मुद्रा में [[शिव]] के नक्काशी बा। {{Sfn|Desai|2007|p=285}} {{Sfn|Bandyopadhyay|2014|p=71}}
== ढांचा ==
[[चित्र:Dwarakadheesh Temple, 2014.jpg|thumb|पांच मंजिल के नजारा]]
ई 72 खंभा पर बनल पाँच मंजिला इमारत हवे (60 खंभा वाला बलुआ पत्थर के मंदिर के भी जिकिर कइल गइल बा)। <ref name="Brit">{{Cite web|url=http://www.britannica.com/EBchecked/topic/174909/Dwarka|title=Dwarka|publisher=Encyclopædia Britannica|access-date=19 April 2015}}</ref> {{Sfn|Desai|2007|p=285}} {{Sfn|Bansal|2008|p=20-23}} मंदिर में दू गो महत्वपूर्ण प्रवेश द्वार बाड़ें, एगो मुख्य प्रवेश द्वार हवे जेकरा के ''मोक्ष द्वार'' (मतलब "मुक्ति के दरवाजा") कहल जाला आ बाहर निकले के दरवाजा जेकरा के ''स्वर्ग द्वार'' ( मतलब: "स्वर्ग के फाटक")। {{Sfn|Bansal|2008|p=20-23}}
गर्भगृह में देवता बनावल मुख्य देवता द्वारकादीश हवें जेकरा के विष्णु के त्रिविक्रम रूप में जानल जाला आ चार गो भुजा के साथ चित्रित कइल गइल बा। {{Sfn|Bansal|2008|p=20-23}} मुख्य वेदी के बाईं ओर के कक्ष पर कृष्ण के बड़ भाई बलराम बाड़ें। दाहिने ओर के कक्ष में कृष्ण के बेटा आ पोता प्रद्युम्न आ अनिरुद्ध के मूर्ति बाड़ी सऽ। केंद्रीय तीर्थ के आसपास कई गो तीर्थ में देवी [[राधा]], जाम्बवती, सत्यभामा आ [[लक्ष्मी]] के मूर्ति बाड़ी स {{Sfn|Bansal|2008|p=20-23}} माधव रावजी (कृष्ण के दूसर नाँव), बलराम आ ऋषि दुर्वासा के तीर्थ भी एह मंदिर में मौजूद बा। {{Sfn|Bandyopadhyay|2014|p=71}} द्वारकाधीश के केंद्रीय तीर्थ के ठीक सामने राधा कृष्ण आ देवकी के समर्पित दू गो अलग-अलग तीर्थ भी बाड़ें
[[चित्र:Dwarakadheesh_temple_in_Dwarka_-_Gujarat,_India_(5933598087).jpg|thumb|प्लेटफार्म के चारों ओर नक्काशी]]
मंदिर के शिखर {{Convert|78|m}} के ऊँचाई तक जाला आ बहुत बड़हन झंडा फहरावल जाला जवना पर [[सुरुज|सूर्य]] आ [[चंद्रमा]] के चिन्ह लगावल गइल बा। {{Sfn|Bansal|2008|p=20-23}} त्रिकोणीय आकार के ई झंडा {{Convert|50|ft}} होला लंबाई के बा। एह झंडा के दिन में चार बेर नया झंडा से बदलल जाला आ हिन्दू लोग नया झंडा खरीद के एकरा के फहरावे खातिर भारी रकम देला एह खाता से मिले वाला पईसा मंदिर के ट्रस्ट फंड में जमा क के मंदिर के संचालन अउरी रखरखाव के खर्चा पूरा कइल जाला। {{Sfn|Bandyopadhyay|2014|p=71}}
== अवार्ड ==
द्वारकाधीश जगत मंदिर के 22 मार्च 2021 के ''वर्ल्ड टैलेंट ऑर्गेनाइजेशन'', न्यू जर्सी, अमेरिका द्वारा "वर्ल्ड अमेजिंग प्लेस" के प्रमाणपत्र से सम्मानित कइल गइल। <ref>{{Cite news|url=https://tv9gujarati.com/gujarat/devbhumi-dwarka/devbhumi-dwarka-dwarkas-jagatmandir-gets-world-amazing-palace-honor-253162.html|title=Devbhumi Dwarka: દ્વારકાના જગતમંદિરને મળ્યું વર્લ્ડ અમેઝિંગ પેલેસનું સન્માન|last=Gujarati|first=TV9|date=22 March 2021|work=Tv9 Gujarati|access-date=23 March 2021|language=gu|trans-title=Devbhumi Dwarka: Dwarka's Jagatmandir honored with World Amazing Palace}}</ref><ref>{{Cite web|url=https://www.worldtalentorg.com/worlds-amazing-place/shree-dwarkadhish-temple/|title=Shree Dwarkadhish Temple {{!}} World Talent Organization|date=13 March 2021|website=worldtalentorg.com|access-date=23 March 2021}}</ref>
== ग्रंथसूची ==
* [http://www.indianexpress.com/ie/daily/20000714/ina14051.html UNESCO World Cultural Heritage Site status, report on Indian Express newspaper website]
* [https://web.archive.org/web/20090112151303/http://www.nio.org/projects/vora/project_vora_1.jsp Underwater remains near Dwarakadheesh temple, on website of National Institute of Oceanography]
== संदर्भ ==
{{Reflist|40em}}
== बाहरी लिंक ==
* [http://www.dwarkadhish.org/ आधिकारिक वेबसाइट]
[[श्रेणी:हिंदू मंदिर]]
[[श्रेणी:चार धाम मंदिर]]
ay4250ixwjplj10vyfral2pjkmos7uu
औद्योगिक प्रशिक्षण संस्थान
0
88201
803340
786567
2026-07-29T16:22:02Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803340
wikitext
text/x-wiki
भारत में '''औद्योगिक प्रशिक्षण संस्थान''' भारत सरकार के श्रम आ रोजगार मंत्रालय के तहत चलावल जाए वाला प्रशिक्षण संस्थान हवें, जवना में प्रशिक्षु लोग के तकनीकी काम में प्रशिक्षित कइल जाला।
==== कारीगर प्रशिक्षण योजना (सीटीएस) ====
प्रशिक्षण महानिदेशालय (डीजीटी) 1950 में कारीगर प्रशिक्षण योजना के सुरुआत कइलस।[4] 130 से अधिका अलग-अलग विशेषता में प्रशिक्षण के अवधि छह महीना से लेके दु साल तक होखेला। कोर्स खातिर पूर्व शर्त 8वीं से लेके 12वीं तक के पास बा। प्रशिक्षण पूरा होखला प प्रशिक्षु अखिल भारतीय व्यापार परीक्षा (एआईटीटी) लिखेले। सफल उम्मीदवारन के राष्ट्रीय व्यापार प्रमाणपत्र (एनटीसी) दिहल जाला.
==== प्रवेश (दाखिला लेबे के बा) ====
आईटीआई/आईटीसी के विभिन्न ट्रेड में प्रवेश हर साल अगस्त में होला। नया सत्र शुरू होखे से पहिले आईटीआई प्रवेश प्रक्रिया शुरू हो जाला। एह योजना के तहत सत्र 1 अगस्त से शुरू हो रहल बा। एनसीवीटी गाइडलाइन के तहत आईटीआई में दाखिला मेरिट आधारित / लिखित परीक्षा प कइल जाला। निजी आईटीआई में प्रवेश सीधे होला लेकिन एकरा में खर्च सरकारी आईटीआई कॉलेज से अधिक होला।
==== आईटीआई ट्रेड्स के नाम ====
{| class="wikitable"
|'''ट्रेड्स के नाम'''
|'''ट्रेड कोड'''
|-
|Additive Manufacturing Technician Three D Printing (NSQF)
|400
|-
|ADVANCED CNC MACHINING TECHNICIAN (NSQF)
|1001
|-
|Aeronautical Structure and Equipment Fitter (NSQF)
|401
|-
|Agro Processing (NSQF)
|402
|-
|Architectural Assistant (NSQF)
|403
|-
|Architectural Draughtsman (NE) (NSQF)
|405
|-
|Architectural Draughtsman (NSQF)
|990
|-
|ARTISAN USING ADVANCED TOOL (NSQF)
|1004
|-
|Attendant Operator (Chemical Plant) (NSQF)
|406
|-
|Baker and Confectioner (NSQF)
|407
|-
|Bamboo Works (NSQF)
|408
|-
|BASIC DESIGNER AND VIRTUAL VERIFIER (MECHANICAL) (NSQF)
|1005
|-
|Cane Willow and Bamboo Worker (NSQF)
|413
|-
|Carpenter (NSQF)
|966
|-
|Catering & Hospitality Assistant (NSQF)
|415
|-
|CENTRAL AIRCONDITION PLANT MECHANIC(NSQF)
|996
|-
|Civil Engineer Assistant (NSQF)
|416
|-
|Computer Aided Embroidery And Designing (NSQF)
|417
|-
|Computer Hardware & Network Maintenance (NSQF)
|418
|-
|Computer Hardware & Networking Maintenance (One Year) (NSQF)
|419
|-
|Computer Operator & Programming Assistant (VI) (NSQF)
|420
|-
|Computer Operator and Programming Assistant
|242
|-
|Computer Operator and Programming Assistant (NSQF)
|421
|-
|Cosmetology (NSQF)
|973
|-
|Cosmetology (NSQF)
|409
|-
|COSMETOLOGY(NSQF)
|999
|-
|Craftsman Food Production (Vegetarian) (NSQF)
|423
|-
|Cutting & Sewing (VI) (NSQF)
|425
|-
|Dairying
|100
|-
|Dairying(NSQF)
|993
|-
|Data Base System Assistant (NSQF)
|427
|-
|Data Entry Operator (NSQF)
|1012
|-
|Dental Laboratory Equipment Technician (NSQF)
|430
|-
|Desk Top Publishing Operator (NSQF)
|431
|-
|Desktop Publishing Operator (VI) (NSQF)
|432
|-
|Digital Photographer (NSQF)
|433
|-
|Draftsman Civil (NSQF)
|436
|-
|Draughtsman (Civil) (DA) (NSQF)
|438
|-
|Draughtsman (Civil) (NSQF)
|991
|-
|Draughtsman (Mechanical) (NSQF)
|439
|-
|Dress Making (NSQF)
|440
|-
|Driver Cum Mechanic (NSQF)
|1011
|-
|Early Childhood Educator
|544
|-
|Early Childhood Educator(NSQF)
|986
|-
|Electrician
|231
|-
|Electrician (DST) (NSQF)
|443
|-
|Electrician (NSQF)
|442
|-
|Electrician Power Distribution (NSQF)
|444
|-
|Electronics Mechanic (NSQF)
|446
|-
|Electroplater
|233
|-
|Electroplater (NSQF)
|447
|-
|Event Management Assitant (NSQF)
|1016
|-
|Fashion Design & Technology (NSQF)
|449
|-
|Fibre to Home Technician (NSQF)
|1017
|-
|Finance Executive (NSQF)
|450
|-
|Fire Technology and Industrial Safety Management (NSQF)
|451
|-
|Firemen (NSQF)
|452
|-
|Fitter
|227
|-
|Fitter (DST) (NSQF)
|454
|-
|Fitter (NSQF)
|453
|-
|Floriculture & Landscaping (NSQF)
|455
|-
|Food & Beverages Services Assistant (NSQF)
|456
|-
|Food Beverage (NSQF)
|457
|-
|Food Production (General) (NSQF)
|458
|-
|Footwear maker (NSQF)
|459
|-
|Foundryman
|214
|-
|Foundryman (NSQF)
|460
|-
|Front Office Assistant (NSQF)
|461
|-
|Fruit and Vegetable Processor (NSQF)
|462
|-
|Geo Informatics Assitant (NSQF)
|463
|-
|Hair & Skin Care (VI) (NSQF)
|464
|-
|Health Safety & Environment (NSQF)
|466
|-
|Health Sanitary Inspector (NSQF)
|467
|-
|Horticulture (NSQF)
|468
|-
|Horticulture (P)
|921
|-
|Hospital House Keeping (NSQF)
|470
|-
|House Keeper (NSQF)
|471
|-
|Human Resource Executive (NSQF)
|472
|-
|Industrial Painter (NSQF)
|473
|-
|INDUSTRIAL ROBOTICS AND DIGITAL MANUFACTURING TECHNICIAN (NSQF)
|1006
|-
|Information Communication Technology System Maintenance (NSQF)
|474
|-
|Information Technology (NSQF)
|475
|-
|Instrument Mechanic (Chemical Plant) (NSQF)
|478
|-
|Instrument Mechanic (NSQF)
|477
|-
|Interior Decoration and Designing (P)
|926
|-
|Interior Design & Decoration (NSQF)
|481
|-
|IoT Technician (Smart Agriculture)(NSQF)
|987
|-
|IoT Technician (Smart City)(NSQF)
|965
|-
|IoT Technician (Smart healthcare)(NSQF)
|988
|-
|Laboratory Assistant (Chemical Plant) (NSQF)
|485
|-
|Laboratory Assistant-Chemical Plant (NSQF)
|486
|-
|Leather Goods Maker (NSQF)
|487
|-
|Lift and Escalator Mechanic (NSQF)
|490
|-
|Lift Mechanic (P)
|929
|-
|Litho- Offset Machine Minder (NSQF)
|492
|-
|Machinist (Grinder) (NSQF)
|494
|-
|Machinist (NSQF)
|493
|-
|Maintenance Mechanic (Chemical Plant) (NSQF)
|495
|-
|MANUFACTURING PROCESS CONTROL AND AUTOMATION (NSQF)
|1007
|-
|Marine Engine Fitter (NSQF)
|496
|-
|Marine Fitter (NSQF)
|497
|-
|Marketing Executive (NSQF)
|498
|-
|Mason (Building Constructor) (NSQF)
|499
|-
|Mech. Repair & Maintenance of Heavy Vehicles (NSQF)
|500
|-
|Mech. Repair & Maintenance of Light Vehicles (NSQF)
|501
|-
|Mechanic (Motor Vehicle) (NSQF)
|502
|-
|Mechanic (Refrigeration and Air-Conditioning) (NSQF)
|503
|-
|Mechanic (Tractor) (NSQF)
|504
|-
|Mechanic Agriculture Machinery (NSQF)
|505
|-
|Mechanic Auto Body Painting (NSQF)
|507
|-
|Mechanic Auto Body Repair (NSQF)
|508
|-
|Mechanic Auto Electrical and Electronics (NSQF)
|509
|-
|Mechanic Communication Equipment Maintenance (NSQF)
|510
|-
|Mechanic Computer Hardware (NSQF)
|511
|-
|Mechanic Consumer Electronics (NSQF)
|512
|-
|Mechanic Consumer Electronics Appliances (NSQF)
|513
|-
|Mechanic Cum Operator Electronics Communication System (NSQF)
|514
|-
|Mechanic Diesel (NSQF)
|515
|-
|MECHANIC ELECTRIC VEHICLE (NSQF)
|1002
|-
|Mechanic Industrial Electronics (NSQF)
|516
|-
|Mechanic Lens/Prism Grinding (NSQF)
|517
|-
|Mechanic Machine Tool Maintenance (NSQF)
|518
|-
|Mechanic Mechatronics (NSQF)
|519
|-
|Mechanic Medical Electronics (NSQF)
|520
|-
|Mechanic Mining Machinery (NSQF)
|521
|-
|Mechanic Radio & T.V. (NSQF)
|523
|-
|Mechanic Two & Three Wheeler (NSQF)
|985
|-
|Metal Cutting Attendant (VI) (NSQF)
|527
|-
|Milk & Milk Product Technician (NSQF)
|994
|-
|Multimedia Animation & Special Effects (NSQF)
|530
|-
|Network Technician (NSQF)
|1015
|-
|Office Assitant cum Computer Operator (NSQF)
|532
|-
|Old Age Care (NSQF)
|533
|-
|Operator Advanced Machine Tools (NSQF)
|535
|-
|Painter General (NSQF)
|536
|-
|Photographer (NSQF)
|539
|-
|Physiotherapy Technician (NSQF)
|540
|-
|Plastic Processing Operator (NSQF)
|541
|-
|Plate Maker-Cum-Impositor (NSQF)
|542
|-
|Plumber (NSQF)
|543
|-
|Pump Operator-Cum-Mechanic (NSQF)
|547
|-
|Radiology Technician (NSQF)
|548
|-
|REFRIGERATION AND AIR CONDITIONING TECHNICIAN(NSQF)
|998
|-
|Remotely Piloted Aircraft (RPA Drone Pilot) (NSQF)
|1014
|-
|Secretarial Practice (English)
|258
|-
|Secretarial Practice (English) (NSQF)
|553
|-
|Sewing Technology (NSQF)
|554
|-
|Sheet Metal Worker (DA) (NSQF)
|556
|-
|Sheet Metal Worker (NSQF)
|555
|-
|Smartphone Technician Cum App Tester (NSQF)
|1013
|-
|Soil Testing and Crop Technician (NSQF)
|559
|-
|Solar Technician
|949
|-
|Solar Technician (Electrical) (NSQF)
|561
|-
|Solar Technician (NSQF)
|560
|-
|Spa Therapy (NSQF)
|562
|-
|Spinning Technician (NSQF)
|563
|-
|Stenographer & Secretarial Assistant (English) (NSQF)
|564
|-
|Stenographer & Secretarial Assistant (Hindi) (NSQF)
|565
|-
|Stone Mining Machine Operator (NSQF)
|566
|-
|Stone Processing Machine Operator (NSQF)
|567
|-
|Surface Ornamentation Techniques (Embroidery) (NSQF)
|568
|-
|Surveyor
|207
|-
|Surveyor (NSQF)
|970
|-
|Surveyor (Two Years)
|974
|-
|Technician Mechatronics (NSQF)
|570
|-
|TECHNICIAN MEDICAL ELECTRONICS(NSQF)
|995
|-
|Technician Power Electronics System (NSQF)
|571
|-
|Textile Mechatronics (NSQF)
|572
|-
|Textile Wet Processing Technician (NSQF)
|573
|-
|Tool & Die Maker (Dies & Moulds) (NSQF)
|574
|-
|Tool & Die Maker (Press Tools, Jigs & Fixtures) (NSQF)
|575
|-
|Tourist Guide (NSQF)
|576
|-
|Travel & Tour Assistant (NSQF)
|577
|-
|Turner (NSQF)
|578
|-
|Vessel Navigator (NSQF)
|579
|-
|Weaving of Woolen Fabrics (NSQF)
|580
|-
|Weaving Technician (NSQF)
|581
|-
|Weaving Technician for Silk & Woolen Fabrics (NSQF)
|582
|-
|Welder
|312
|-
|Welder (DA) (NSQF)
|585
|-
|Welder (Fabrication & Fitting) (NSQF)
|586
|-
|Welder (GMAW & GTAW) (NSQF)
|992
|-
|Welder (NSQF)
|969
|-
|Welder (Pipe) (NSQF)
|588
|-
|Welder (Structural) (NSQF)
|589
|-
|Welder (Welding & Inspection) (NSQF)
|590
|-
|Wireman (NSQF)
|592
|}
==== एनसीवीटी आईटीआई सीबीटी परीक्षा के तैयारी कईसे कइल जाला? ====
ई सवाल ओह विद्यार्थियन से उठत बा. जे आईटीआई एडमिशन लेले बा आ अब उनकर परीक्षा नजदीक आ गइल बा। वैसे हम बता दीं कि हम आईटीआई भी कइले बानी। हम एनसीवीटी आईटीआई सीबीटी परीक्षा के भी तैयारी ऑनलाइन कइले रहनी, उहो मुफ्त में, हालांकि रउआ कई तरह से ऑनलाइन तैयारी कर सकतानी। बाकिर जवना वेबसाइट से हम तइयारी करत रहीं ओकर नाम रहे [https://www.testgive.com/ TestGive.com] {{Webarchive|url=https://web.archive.org/web/20230922155127/https://www.testgive.com/ |date=2023-09-22 }} इहाँ से मॉक टेस्ट देके रउआ अपना परीक्षा के तैयारी कर सकतानी।
बता दीं कि आईटीआई सीबीटी परीक्षा के तइयारी खातिर Bharat Skills पर निमी क्वेश्चन बैंक जारी होला. ऊ सब सवाल एह मॉक टेस्ट में रहेला।
==== शिक्षुता प्रशिक्षण योजना ====
आईटीआई पूरा कइला के बाद व्यापारी अलग-अलग उद्योग में अप्रेंटिसशिप के प्रशिक्षण ले सकतारे। एह योजना के कौशल विकास आ उद्यमिता मंत्रालय के तहत प्रशिक्षण महानिदेशालय द्वारा एह उद्देश्य से लागू कइल जाला कि "कौशल हासिल करे खातिर अकेले संस्थान सभ में दिहल जाए वाला प्रशिक्षण पर्याप्त ना होला आ वास्तविक कामकाजी जगह पर प्रशिक्षण से एकर पूरक होखे के जरूरत बा।
अप्रेंटिसशिप प्रशिक्षण के अवधि आम तौर प आईटीआई के अंतिम परीक्षा में आवे वाला लोग खातिर एक साल के होखेला, यानी एआईटीटी। फ्रेशर लोग खातिर आमतौर पर तीन साल लागेला। प्रशिक्षण के दौरान प्रशिक्षु लोग के हर महीना वजीफा दिहल जाला।
o86trs6z99bms3chhrjdrxoi8b90j96
बद्रीनाथ मंदिर
0
88235
803368
783706
2026-07-29T16:24:13Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803368
wikitext
text/x-wiki
'''बद्रीनाथ''' या '''बद्रीनारायण मंदिर''' [[विष्णु]] के समर्पित एगो [[हिंदू मंदिर]] ह। ई [[उत्तराखंड|भारत के उत्तराखंड]] राज्य के बद्रीनाथ शहर में स्थित बा। ई मंदिर विष्णु के समर्पित 108 दिव्य देश में से एगो भी बा - वैष्णव लोग खातिर पवित्र तीर्थ - जिनके बद्रीनाथ के रूप में पूजल जाला। ई हर साल छह महीना (अप्रैल के अंत से नवंबर के सुरुआत के बीच) खुलल रहे ला, काहें से कि [[हिमालय|हिमालयी]] इलाका में मौसम के चरम स्थिति होला। ई मंदिर [[चमोली जिला]] के गढ़वाल पहाड़ी पटरी में [[अलकनंदा]] नदी के किनारे बा। ई भारत के सभसे ढेर यात्रा कइल जाए वाला तीर्थ केंद्र सभ में से एक बाटे, 2022 में महज 2 महीना में 28 लाख (28 लाख) लोग के दौरा इहाँ भइल बाटे।<ref>{{Cite news|url=https://timesofindia.indiatimes.com/videos/toi-original/uttarakhand-28-lakh-pilgrims-visit-char-dham-in-60-days-choppers-flying-like-auto-rickshaws-experts-warn-of-consequences/videoshow/93475084.cms|title=Uttarakhand: 28 lakh pilgrims visit Char Dham in 60 days, choppers flying like auto-rickshaws, experts warn of consequences|date=10 August 2022|work=The Times of India|access-date=12 August 2022}}</ref>
{{Infobox Hindu temple
|name = बद्रीनाथ मंदिर
|image = Badrinath Temple , Uttarakhand.jpg
|alt = Image of the temple with steps leading to the entrance
|caption = The temple with steps leading to the entrance
|coordinates ={{coord|30.744695|79.491175|type:landmark_region:IN|display=inline,title}}
|map_type = India Uttarakhand
|map_caption = उत्तराखंड में स्थिति
|country = {{IND}}
|state = [[Uttarakhand]]
|district = [[Chamoli district]]
|location = [[Badrinath]]
|elevation_m = 3100
|architecture =
|inscriptions =
|website = {{url|badrinath-kedarnath.gov.in}}
}}
मंदिर में पूजल जाए वाला मुख्य देवता के मुर्ती एगो {{Convert|1|ft|abbr=on}}, बद्रीनारायण के रूप में विष्णु के करिया ग्रेनाइट के बनल मुर्ती हवे। एह देवता के कई हिंदू लोग आठ गो ''स्वयं व्यक्त क्षेत्र'', या विष्णु के स्व-प्रकट देवता सभ में से एक माने ला।
[[गंगा नदी|गंगा]] नदी के धरती माई पर उतरे के याद करे वाला "माता मूर्ति क मेला" बद्रीनाथ मंदिर में मनावल जाए वाला सबसे प्रमुख परब ह। हालांकि बद्रीनाथ उत्तर भारत में स्थित बा, लेकिन मुख्य पुजारी या रावल परंपरागत रूप से दक्षिण भारतीय राज्य [[केरल]] से चुनल नम्बुदिरी ब्राह्मण होखे लें। एह मंदिर के [[उत्तर प्रदेश]] राज्य सरकार के एक्ट नंबर 30/1948 में एक्ट नंबर 16,1939 में शामिल कइल गइल जवना के बाद में श्री बदरिनाथ आ श्री [[केदारनाथ मंदिर|केदारनाथ]] मंदिर एक्ट के नाम से जानल गइल। राज्य सरकार के ओर से नामित समिति दुनो मंदिर के प्रबंधन करेले अउरी एकरा बोर्ड में सतरह सदस्य बाड़े।
[[चित्र:Devotees_at_the_Badrinath_Temple_in_Uttarakhand,_India.jpg|thumb|अक्टूबर 2022 में बद्रीनाथ मंदिर में भक्त]]
''विष्णु पुराण'' आ ''स्कंद पुराण'' जइसन प्राचीन धार्मिक ग्रंथन में एह मंदिर के जिक्र बा। एकर महिमा ''नालायिरा दिव्य प्रबंधम'', 6वीं–9वीं सदी ईसवी के अल्वार संत लोग के सुरुआती मध्यकालीन तमिल ग्रंथ में बर्णित बा।
== स्थान, वास्तुकला, आ तीर्थ सभ ==
[[चित्र:Badrinath-taptkund.jpg|alt=a rectangular tank with a house on the bank and with people taking bath in the hot springs|left|thumb|200x200px| बद्रीनाथ मंदिर के बगल में तप्त कुंड हॉट स्प्रिंग्स, स्नान घर के अंदर]]
ई मंदिर [[उत्तराखंड]] के [[चमोली जिला]] में [[अलकनंदा|अलकनंदा नदी]] {{Sfn|Baynes|1878}} के किनारे गढ़वाल पहाड़ी इलाका में बा। पहाड़ी इलाका औसत समुद्र तल से ऊपर {{Convert|3133|m|ft|abbr=on}} पर स्थित बा। <ref name="about"/> मंदिर के सामने नर परबत पर्वत बा, जबकि नीलकंठ चोटी के पीछे नारायण परबत बा।
आदि शंकर नौवीं सदी में बद्रीनाथ के तीर्थ स्थल के रूप में स्थापित कइलें। मंदिर के तीन गो संरचना बा: गर्भगृह, [[हिंदू दर्शन|दर्शन]] मंडप (पूजा भवन), आ सभा मंडप (सम्मेलन हॉल)। <ref name="about"/> गर्भगृह के शंक्वाकार आकार के छत, ''गर्भगृह'', लगभग {{Convert|15|m|ft|abbr=on}} लंबा बाटे आ ऊपर से एगो छोट गुंबद बा जे सोना के मढ़ल छत से ढंकल बा। <ref name="Nair67" /> सोझा के मुख्य साइड पत्थर से बनल बा आ मेहराबदार खिड़की बाड़ी सऽ। एगो चौड़ा सीढ़ी मुख्य प्रवेश द्वार पर पहुँचे ले, ई एगो ऊँच, मेहराबदार गेटवे हवे। ठीक भीतर एगो मंडप, एगो बड़हन, खंभा वाला हॉल बा जे गर्भगृह में जाला। हॉल के देवाल आ खंभा पर जटिल नक्काशी कइल गइल बा।
गरभगृह में {{Convert|1|ft|abbr=on}} के बद्रीनारायण के शालीग्राम (काला पत्थर के) मुर्ती बाटे जवन बद्री के पेड़ के नीचे सोना के छतरी में रखल बा। बद्रीनारायण के देवता में ऊ अपना दू गो बाँहि में ''[[शंख]]'' (शंख) आ ''[[सुदर्शन चक्र|चक्र]]'' (चक्र) के उठावल मुद्रा में आ बाकी दू गो बाँह ''योगमुद्रा'' ( ''पद्मासन'' ) मुद्रा में गोदी में टिकल देखावल गइल बा। <ref name="about">{{Cite web|url=http://www.badarikedar.org/badrinath.aspx|title=About the temple|year=2006|publisher=Shri Badrinath - Shri Kedarnath Temples Committee|archive-url=https://web.archive.org/web/20131214005611/http://badarikedar.org/badrinath.aspx|archive-date=14 December 2013|access-date=1 January 2014}}</ref> एह गर्भगृह में धन के देवता {{Em dash}} कुबेर, नारद ऋषि, उद्धव, नर आ नारायण के मूर्ति भी बाड़ी स। पन्द्रह गो अउरी मूर्ति बाड़ी सऽ जिनहन के पूजा मंदिर के आसपास कइल जाला। इनहन में [[लक्ष्मी]] (विष्णु के पत्नी), गरुड़ (नारायण के वाहन ), आ [[नव दुर्गा|नवदुर्गा]], [[दुर्गा]] के नौ गो अलग-अलग रूप में प्रकटीकरण सामिल बाड़ें। मंदिर में लक्ष्मी नरसिंहर के तीर्थ आ संत लोग खातिर [[आदि शंकर]] (ई.स 788-820), नर आ नारायण, घंटकरन, वेदांत देसिका आ रामानुजाचार्य के अस्थान बाटे। मंदिर के सभ देवता करिया पत्थर से बनल बाड़ें। <ref name="about" />
मंदिर के ठीक नीचे गरम सल्फर के झरना सभ के समूह तप्त कुंड के औषधीय परभाव वाला मानल जाला; कई तीर्थयात्री लोग मंदिर में जाए से पहिले झरना में नहाए के जरूरत मानेला। झरना सभ के तापमान साल भर {{Convert|55|°C|°F|abbr=on}} होला, जबकि बाहरी तापमान आमतौर पर {{Convert|17|°C|°F|abbr=on}} से नीचे होला। <ref name="about"/> मंदिर में दू गो पानी के पोखरा के नारद कुंड आ सूर्य कुंड कहल जाला।
== इतिहास ==
मंदिर के बारे में कवनो ऐतिहासिक रिकार्ड नइखे, बाकिर [[वेद|वैदिक ग्रंथन]] में एहिजे के मुख्य देवता बद्रीनाथ के उल्लेख मिले ला ( {{Circa|1750|lk=no}} –500 ईसा पूर्व के बा)। कुछ विवरणन के अनुसार [[वैदिक काल]] में एह तीर्थ में कवनो ना कवनो रूप में पूजा कइल जात रहे। ई मंदिर 8वीं सदी ले बौद्ध तीर्थ रहल आ [[आदि शंकर]] एह तीर्थ के फेर से जिंदा क के हिंदू मंदिर में बदल दिहलें। मंदिर के आर्किटेक्चर [[बौद्ध धर्म|बौद्ध]] विहार से मिलत जुलत आ चमकदार रंग से रंगल साम्हने के साइड जे बौद्ध मंदिर सभ के बिसेसता हवे, एह तर्क के पुष्टी करे ला। अउरी बिबरन सभ में बतावल गइल बा कि मूल रूप से एकर स्थापना आदिशंकर द्वारा नौवीं सदी में तीर्थस्थल के रूप में कइल गइल रहे। मानल जाला कि शंकर ई. 814 से 820 तक छह साल ले एह जगह पर रहलें। छह महीना बद्रीनाथ में आ बाकी साल केदारनाथ में रहलन। हिंदू अनुयायी लोग के कहनाम बा कि ऊ अलकनंदा नदी में बद्रीनाथ के देवता के मुर्ती के खोज कइलें आ एकरा के तप्त कुंड हॉट स्प्रिंग्स के लगे एगो गुफा में अस्थापित कइलें। एगो पारंपरिक कहानी में ई दावा कइल गइल बा कि शंकर एह इलाका के सगरी बौद्ध लोग के परमार शासक राजा कनक पाल के मदद से भगा दिहलें। राजा के वंशानुगत उत्तराधिकारी लोग मंदिर के शासन करत रहे आ एकर खरचा पूरा करे खातिर गाँव के रकम देवे। मंदिर के रास्ता पर पड़े वाला गाँव सभ के एगो समूह से मिले वाला आमदनी के इस्तेमाल तीर्थयात्री लोग के खाना खियावे आ रहे खातिर कइल जात रहे। परमार के शासक लोग "बोलंदा बद्रीनाथ" के उपाधि रखले रहे, मतलब बोले वाला बद्रीनाथ। इनकर अउरी उपाधि रहे जवना में श्री 108 बसदृष्टाचार्यपरायण गढ़राज महिमाहेन्द्र, धर्मबीभाब, आ धर्मरक्षक सिगमानी शामिल रहे।
बद्रीनाथ के सिंहासन के नाँव एहिजा के मुख्य देवता के नाँव पर रखल गइल; राजा तीर्थ के ओर जाए से पहिले भक्त लोग के विधिवत नमन के आनंद लेत रहले। 19वीं सदी के अंत तक ई प्रथा जारी रहल। 16वीं सदी के दौरान गढ़वाल के राजा मूर्ती के वर्तमान मंदिर में ले गइलें। जब गढ़वाल राज्य के बंटवारा भइल त बद्रीनाथ मंदिर अंग्रेज के शासन में आ गइल लेकिन गढ़वाल के राजा प्रबंधन समिति के अध्यक्ष के रूप में जारी रहलें। पुजारी के चयन गढ़वाल आ त्रावणकोर राजपरिवार के बीच सलाह-मशविरा के बाद कइल जाला। <ref name="Guha" />
मंदिर के उमिर आ हिमस्खलन से नोकसान के कारन एह मंदिर के कई गो बड़हन मरम्मत भइल बा। 17वीं सदी में गढ़वाल के राजा लोग द्वारा मंदिर के बिस्तार कइल गइल। 1803 के गढ़वाल के भयानक भूकंप के दौरान काफी नुकसान के बाद एकर बहुत हद तक जयपुर के राजा द्वारा दोबारा बनावल गइल। एकर मरम्मत अबहिन ले 1870 के दशक में भइल रहे{{Sfn|Baynes|1878}} बाकी [[पहिला बिस्व जुद्ध]] के समय ले ई सभ पूरा हो गइल रहलें। {{Sfn|Chisholm|1911}} ओह समय ई शहर अबहिन छोट रहल, एह में खाली 20-अजीब झोपड़ी सभ रहलीं जिनहन में मंदिर के कार्यकर्ता पंडा लोग रहे, बाकी तीर्थयात्री लोग के संख्या आमतौर पर सात से दस हजार के बीच रहे।{{Sfn|Chisholm|1911}} हर बारह साल पर होखे वाला कुंभा मेला महोत्सव में आगंतुकन के संख्या बढ़ के 50,000 हो गइल।{{Sfn|Chisholm|1911}} एह मंदिर के बिबिध राजा लोग द्वारा वसीयत कइल गइल बिबिध गाँव सभ के किराया से भी राजस्व मिलत रहे। {{Sfn|Baynes|1878}}
साल 2006 के दौरान राज्य सरकार अवैध अतिक्रमण प लगाम लगावे खाती बद्रीनाथ के आसपास के इलाका के नो कंस्ट्रक्शन जोन के घोषणा कइलस।
== दंतकथा ==
[[चित्र:Nara_narayana.jpg|thumb|नर आ नारायण]]
हिंदू कथा के अनुसार देवता [[विष्णु]] एही जगह पर ध्यान में बइठल रहलें। ध्यान के दौरान विष्णु ठंडा के मौसम से अनजान रहले। [[लक्ष्मी]], उनकर पत्नी, बदरी के पेड़ (जेकरा के भोजपुरी में 'बइर' कहल जाला) के रूप में उनकर रक्षा कइली। लक्ष्मी के भक्ति से प्रसन्न होके विष्णु एह जगह के नाम ''बद्रिका आश्रम रखले'' । एटकिन्सन (1979) के अनुसार ई जगह पहिले बइर के जंगल रहे, जवन आज उहाँ ना पावल जाला। मंदिर में बद्रीनाथ के रूप में विष्णु के ''पदमासन'' मुद्रा में बइठल चित्रित कइल गइल बा। किंवदंती के अनुसार विष्णु के नारद ऋषि ताड़ दिहले रहले, जे विष्णु के पत्नी लक्ष्मी के गोड़ के मालिश करत देखले रहले। विष्णु तप करे खातिर बद्रीनाथ गइलें, बहुत देर तक पदमासन में ध्यान कइलें।
''विष्णु पुराण'' में बद्रीनाथ के उत्पत्ति के एगो अउरी संस्करण बतावल गइल बा। परंपरा के अनुसार यम के दू गो बेटा नरा आ नारायण रहलें {{Em dash}} ई दुनों हिमालय के पहाड़ सभ के आधुनिक नाँव हवें। ऊ लोग आपन धर्म फइलावे खातिर जगह चुनल आ हर केहू हिमालय के विशाल घाटी से बियाह कइल। आश्रम के स्थापना खातिर आदर्श जगह खोजत घरी ओह लोग के पंच बदरी के बाकी चार गो बद्री, अर्थात बृधा बद्री, योग बद्री, ध्यान बद्री आ भविष्य बद्री से भेंट भइल। आखिरकार अलकनंदा नदी के पीछे के गरम ठंडा झरना मिल गइल अउरी एकर नाम '''"''' बद्री विशाल" रखलें।
== तीर्थ यात्रा ==
{{Char Dham}}
{{Historical populations|1990|362757|1995|275900|2000|735200|2005|566524|2010|921950|2012|1060000}}
हर आस्था आ हिंदू धर्म के सभ विचारधारा के भक्त लोग बद्रीनाथ मंदिर में आवेलें। <ref>[[Badrinath Temple#Rao|Rao 2008]], p. 474</ref> काशी मठ,<ref>{{Cite web|url=http://www.kashimath.org/branches/|title=Kashi Math at Badrinath|publisher=Shree Kashi Math Samasthanam|access-date=10 September 2014|archive-date=8 August 2018|archive-url=https://web.archive.org/web/20180808203059/https://www.kashimath.org/branches/|url-status=dead}}</ref> जेयर मठ (आंध्र मठ),<ref>{{Cite web|url=http://www.chinnajeeyar.org/main/content/badari-ashtakshari-kshethriya-annadana-sakha-sangham-bakass/|title=Badari Ashtakshari Kshethriya Annadana Sakha Sangham, (BAKASS)|publisher=Chinna Jeeyar Mutt|archive-url=https://web.archive.org/web/20140910201610/http://www.chinnajeeyar.org/main/content/badari-ashtakshari-kshethriya-annadana-sakha-sangham-bakass/|archive-date=10 September 2014|access-date=10 September 2014}}</ref> उडुपी पेजावर <ref>{{Cite web|url=http://www.vishveshavani.com/contact/|title=Udupi Mutt at Badrinath|publisher=Pejavara Adhokshaja Matha, Udupi|access-date=10 September 2014|archive-date=8 August 2018|archive-url=https://web.archive.org/web/20180808171742/http://www.vishveshavani.com/contact/|url-status=dead}}</ref> आ मंतरलायम श्री राघवेन्द्र स्वामी मठ <ref>{{Cite web|url=http://www.raghavendramutt.org/node/38|title=Raghavendra Mutt Branches|publisher=Raghavendra Mutt|archive-url=https://web.archive.org/web/20140922161955/http://www.raghavendramutt.org/node/38|archive-date=22 September 2014|access-date=10 September 2014}}</ref> नियर सगरी प्रमुख मठ संस्थान सभ के शाखा आ गेस्ट हाउस एहिजा बा।
बद्रीनाथ मंदिर पाँच बदरी नाँव के पाँच गो संबंधित तीर्थ सभ में से एक हवे जे विष्णु के पूजा खातिर समर्पित बाड़ें। पांच मंदिर बा विशाल बद्री - बद्रीनाथ में बद्रीनाथ मंदिर, पांडुकेश्वर में स्थित योगध्यान बद्री, {{Convert|17|km|mi|1|abbr=on}} स्थित भविष्य बद्री ज्योतिर्मथ से सुबैन, वृध बद्री स्थित {{Convert|7|km|mi|1|abbr=on}} अनिमाठ में ज्योतिर्मथ से आ आदि बद्री से {{Convert|17|km|mi|1|abbr=on}} [[कर्णप्रयाग]] से मिलल बा। ई मंदिर सभसे पबित्र हिंदू ''चार धाम'' (चार गो दिव्य) स्थल सभ में से एक मानल जाला, जहाँ रामेश्वरम, बद्रीनाथ, [[पुरी]] आ द्वारका सामिल बाड़ें। हालांकि मंदिर के उत्पत्ति के बारे में साफ-साफ जानकारी नइखे, लेकिन आदिशंकर के ओर से स्थापित हिंदू धर्म के अद्वैत स्कूल चार धाम के उत्पत्ति के श्रेय द्रष्टा के बतावेले। ई चारो मठ भारत के चारो कोना में बाड़ें आ इनहन के अनुचर मंदिर उत्तर में बद्रीनाथ में बद्रीनाथ मंदिर, पूरब में पुरी में [[जगन्नाथ मंदिर, पुरी|जगन्नाथ मंदिर]], पश्चिम में द्वारका में [[द्वारिकाधीश मंदिर|द्वारकाधीश मंदिर]] आ दक्खिन में रामेश्वरम, [[तमिलनाडु]] में रामेश्वरम बाड़ें। <ref name="Chakra" /><ref name="Mittal" />
भले वैचारिक रूप से मंदिर सभ हिंदू धर्म के संप्रदाय, यानी [[शैव मत|शैव]] आ वैष्णव धर्म के बीच बँटल बाड़ें, चार धाम के तीर्थयात्रा एगो अखिल हिन्दू मामला हवे। हिमालय में चार गो निवासस्थान बाड़ें जिनहन के छोट चार धाम कहल जाला: बद्रीनाथ, केदारनाथ, [[गंगोत्री]] आ यमुनोत्री {{Em dash}} ई सभ हिमालय के तलहटी में पड़े लें। मूल चार धाम सभ के बिभेद करे खातिर 20वीं सदी के बीच में ''छोटा'' नाँव जोड़ल गइल। आधुनिक समय में जइसे-जइसे एह जगहन के तीर्थयात्री लोग के संख्या बढ़ल बा, एकरा के हिमालयन चार धाम कहल जाला।
भारत के चार गो कार्डिनल प्वाइंट के पार के यात्रा के हिन्दू लोग पवित्र मानेला, जे लोग अपना जीवन में एक बेर एह मंदिरन के दौरा करे के आकांक्षा राखेला। परंपरागत रूप से ई तीर्थयात्रा पुरी से पूरबी छोर से शुरू होले आ घड़ी के दिशा में अइसन तरीका से आगे बढ़े ले जेकर पालन आमतौर पर हिंदू मंदिर सभ में परिक्रमा खातिर कइल जाला। <ref name="Gwynne" />
== परब-त्यौहार आ धार्मिक प्रथा ==
[[चित्र:Badri_Night.JPG|alt=image of the temple at night illuminated with green light|thumb| रात में बद्रीनाथ मंदिर]]
बद्रीनाथ मंदिर में आयोजित सबसे प्रमुख परब माता मूर्ति का मेला ह जवन [[गंगा नदी|गंगा]] नदी के धरती माई प उतरे के याद में मनावल जाला। पार्थिव प्राणी सब के भलाई खातिर नदी के बारह नाला में बाँटल मानल जाए वाली बद्रीनाथ के महतारी के पूजा एह परब में कइल जाला। जहाँ नदी बहत रहे उ जगह बद्रीनाथ के पवित्र भूमि बन गइल। <ref name="festivals">{{Cite web|url=http://www.badarikedar.org/content-badari.aspx?id=11|title=Festivals celebrated in the temple|year=2006|publisher=Shri Badrinath - Shri Kedarnath Temples Committee|archive-url=https://web.archive.org/web/20120217172228/http://www.badarikedar.org/content-badari.aspx?id=11|archive-date=17 February 2012|access-date=1 January 2014}}</ref>
[[Badri Kedar|बदरी केदार]] परब जून महीना में मंदिर आ केदारनाथ मंदिर दुनों में मनावल जाला। ई परब आठ दिन ले चले ला; एह दौरान देश भर के कलाकार आपन प्रस्तुति देवे लें।<ref name="festivals"/>
रोज सबेरे होखे वाला प्रमुख धार्मिक गतिविधि (या ''पूजा'' ) में ''महाभिषेक'', ''अभिषेक, गीतापाठ'' ''आ'' ''भागवत पूजा'' होला जबकि शाम के ''पूजा'' में ''गीत गोविंद'' आ ''आरती'' शामिल होला। ''अष्टोत्रम'' आ ''सहस्रनाम'' जइसन वैदिक स्त्रोत पाठ सगरी संस्कारन के दौरान कइल जाला। ''आरती'' के बाद बद्रीनाथ के छवि से सजावट हटा के ओकरा पर चंदन के लेप लगावल जाला। छवि से लेप अगिला दिने ''निर्मलय [[हिंदू दर्शन|दर्शन]]'' के दौरान प्रसाद के रूप में भक्त लोग के दिहल जाला। कुछ हिंदू मंदिरन के बिपरीत सभ संस्कार भक्तन के सोझा कइल जाला, जहाँ कुछ प्रथा ओह लोग से छिपल रहेला। चीनी के गोला आ सूखल पतई भक्त लोग के दिहल आम प्रसाद ह। मई 2006 से स्थानीय रूप से तैयार आ स्थानीय बांस के टोकरी में पैक कइल पंचमृत प्रसाद के चढ़ावे के प्रथा शुरू हो गइल।
मंदिर [[Bhatridwityia or later|भतृद्वितिया के शुभ दिन भा बाद]] में अक्टूबर–नवंबर के दौरान जाड़ा खातिर बंद रहेला। <ref name="close" /> बंद होखे के दिन ''अखंड ज्योति'', छह महीना तक चले खातिर घीव भर के दीप जरावल जाला। <ref name="Bhalla258" /> ओह दिन मुख्य पुजारी द्वारा तीर्थयात्री आ मंदिर के अधिकारी लोग के मौजूदगी में विशेष ''पूजा'' कइल जाला। <ref>{{Cite news|url=http://zeenews.india.com/news/uttarakhand/badrinath-shrine-closes-marking-end-of-chardham-yatra_742346.html|title=Badrinath shrine closes marking end of Chardham Yatra|date=17 November 2011|access-date=28 April 2014|publisher=Zeenews|location=Dehradun}}</ref> बद्रीनाथ के छवि के काल्पनिक रूप से एह दौरान ज्योतिर्मठ के नरसिंह मंदिर में स्थानांतरित कइल जाला, जवन {{Convert|40|mi|km|abbr=on}} मंदिर से दूर बा। मंदिर के अप्रैल–मई के आसपास अक्षय तृतीया के फेर से खोलल जाला, जवन [[हिंदू पतरा|हिन्दू कैलेंडर]] के एगो अउरी शुभ दिन ह। <ref name="close">{{Cite news|url=http://timesofindia.indiatimes.com/india/Badrinath-shrine-closed-for-winter/articleshow/3724410.cms|title=Badrinath shrine closed for winter|date=18 November 2008|work=TNN|access-date=28 April 2014|publisher=The Times of India|location=Dehradun}}</ref> जाड़ा के बाद मंदिर के उद्घाटन के पहिला दिन तीर्थयात्री लोग ''अखंड ज्योति'' के साक्षी बने खातिर जुटेला।
ई मंदिर ओह पवित्र जगहन में से एगो ह जहाँ हिंदू लोग पुजारी लोग के मदद से पुरखा लोग के चढ़ावा चढ़ावेला। भक्त लोग मंदिर में जाके गर्भगृह में बद्रीनाथ के प्रतिमा के सामने पूजा करेला अउरी अलकनंदा नदी में पवित्र डुबकी लगावेला। आम मान्यता बा कि डुबकी लगावे से आत्मा शुद्ध हो जाला।
== प्रशासन आ भ्रमण ==
[[चित्र:Alaknanda.jpg|alt=image of Alaknanda river with a hanging bridge across it|left|thumb|200x200px| बद्रीनाथ में [[अलकनंदा|अलकनंदा नदी]]]]
बद्रीनाथ मंदिर के उत्तर प्रदेश राज्य सरकार अधिनियम संख्या 30/1948 में अधिनियम संख्या 16,1939 के रूप में शामिल कइल गइल। जवना के बाद में श्री बदरिनाथ आ श्री केदारनाथ मंदिर एक्ट के नाम से जानल गइल। उत्तराखंड राज्य सरकार के ओर से नामित एगो समिति दुनो मंदिर के प्रबंधन करेले। एह कानून में 2002 में संशोधन क के अतिरिक्त समिति सदस्यन के नियुक्ति कइल गइल जेह में सरकारी अधिकारी आ एगो उपाध्यक्ष शामिल रहलें। <ref>{{Cite web|url=http://www.badarikedar.org/content-badari.aspx?id=2|title=Administration of the temple|year=2006|publisher=Shri Badrinath - Shri Kedarnath Temples Committee|archive-url=https://web.archive.org/web/20131029193204/http://www.badarikedar.org/content-badari.aspx?id=2|archive-date=29 October 2013|access-date=1 January 2014}}</ref> बोर्ड में सतरह गो सदस्य बाड़ें; तीन गो के चयन उत्तराखंड विधान सभा द्वारा, एक-एक सदस्य के चयन [[चमोली जिला|चमोली]] पौड़ी [[पौड़ी गढ़वाल जिला|गढ़वाल]], टिहरी [[टिहरी गढ़वाल जिला|गढ़वाल]] आ [[उत्तरकाशी जिला|उत्तरकाशी]] जिला के जिला परिषद द्वारा, आ दस सदस्य उत्तराखंड सरकार द्वारा नामित होखे लें। <ref>{{Cite web|url=http://www.badarikedar.org/content-badari.aspx?id=3|title=Committee members of the temple|year=2006|publisher=Shri Badrinath - Shri Kedarnath Temples Committee|archive-url=https://web.archive.org/web/20131029184513/http://www.badarikedar.org/content-badari.aspx?id=3|archive-date=29 October 2013|access-date=1 January 2014}}</ref>
जइसन कि मंदिर के रिकार्ड में संकेत दिहल गइल बा कि मंदिर के पुजारी शिव तपस्वी रहले जेकरा के दंडी संन्यासी कहल जाला, जे आधुनिक [[केरल]] में आम धार्मिक समूह नम्बुदिरी समुदाय से संबंधित रहले। जब 1776 ई. में तपस्वी लोग में से आखिरी आदमी के बिना वारिस के मौत हो गइल त गढ़वाल के राजा केरल से गैर-तपस्वी नम्बुदिरी लोग के पुरोहिताई खातिर बोलवले, ई प्रथा आधुनिक काल में भी जारी बा। <ref>{{Cite web|url=http://www.badarikedar.org/content-badari.aspx?id=4|title=Religious setup of the temple|year=2006|publisher=Shri Badrinath - Shri Kedarnath Temples Committee|archive-url=https://web.archive.org/web/20131029192556/http://www.badarikedar.org/content-badari.aspx?id=4|archive-date=29 October 2013|access-date=1 January 2014}}</ref> 1939 तक मंदिर में भक्त लोग के ओर से दिहल गइल सभ दान रावल (मुख्य पुजारी) के लगे चलत रहे, लेकिन 1939 के बाद उनुकर अधिकार क्षेत्र धार्मिक मामला तक सीमित रह गइल। <ref name="Lochtefeld" /> मंदिर के प्रशासनिक संरचना में राज्य सरकार के आदेश के निष्पादन करे वाला एगो मुख्य कार्यकारी अधिकारी, उप मुख्य कार्यकारी अधिकारी, दू गो ओएसडी, एगो कार्यकारी अधिकारी, एगो खाता अधिकारी, एगो मंदिर अधिकारी, आ एगो सार्वजनिक अधिकारी शामिल होलें जे मुख्य कार्यकारी अधिकारी के सहायता करेलें । <ref>{{Cite web|url=http://www.badarikedar.org/content-badari.aspx?id=6|title=Power structure of the temple|year=2006|publisher=Shri Badrinath - Shri Kedarnath Temples Committee|archive-url=https://web.archive.org/web/20131029184226/http://www.badarikedar.org/content-badari.aspx?id=6|archive-date=29 October 2013|access-date=1 January 2014}}</ref>
[[चित्र:Badrinath_Temple_-_OCT_2014.jpg|alt=image showing Badrinath temple with the mountain in the background|thumb|200x200px| गर्मी के दौरान मंदिर के नजारा]]
हालांकि बद्रीनाथ उत्तर भारत में स्थित बा, लेकिन मुख्य पुजारी या रावल परंपरागत रूप से दक्षिण भारतीय राज्य [[केरल]] से चुनल नम्बुदिरी ब्राह्मण होखे लें। मानल जाला कि एह परम्परा के शुरुआत आदि शंकर के कइल गइल जे दक्षिण भारतीय दार्शनिक रहलें। रावल के निहोरा उत्तराखंड सरकार केरल सरकार से कइल गइल बा। उम्मीदवार के [[संस्कृत]] में आचार्य (पोस्ट ग्रेजुएट) के डिग्री होखे के चाहीं, स्नातक होखे के चाहीं, ''मंत्र'' (पवित्र ग्रंथ) पाठ में निपुण होखे के चाहीं आ हिंदू धर्म के वैष्णव संप्रदाय के होखे के चाहीं। गढ़वाल के पूर्व शासक जे बद्रीनाथ के संरक्षक प्रमुख बाड़े, केरल सरकार के भेजल उम्मीदवार के मंजूरी दे देले। रावल के स्थापना खातिर तिलक समारोह होला आ जब मंदिर खुलल रहेला त अप्रैल से नवम्बर ले उनुका के प्रतिनियुक्त कइल जाला। रावल के परम पवित्रता के दर्जा (हिज होलीनेस) गढ़वाल राइफल्स अउरी उत्तराखंड के राज्य सरकार देले बिया। नेपाल के राजशाही भी उनका के बहुत सम्मान देला। अप्रैल से नवम्बर ले मंदिर के पुजारी के रूप में आपन कर्तव्य निभावेलें। एकरा बाद या त ज्योतिर्मठ में रहेलें या फिर केरल में अपना पैतृक गाँव वापस आ जालें। रावल के ड्यूटी रोज सबेरे 4 बजे ''अभिषेक'' के साथ शुरू होला। उनुके द्वारा वामन द्वादशी तक नदी ना पार करे के चाहीं आ ''[[ब्रह्मचर्य|ब्रह्माचार्य]]'' के पालन करे के चाहीं। रावल के सहायता चमोली जिला के गाँव डिम्मार से संबंधित गढ़वाली डिमरी ब्राह्मण, नायब रावल, धर्माधिकारी, वेदपाठी, पुजारी के एगो समूह, पंडा समाधि, भंडारी, रसोइया, भक्ति गायक, देवश्रम के चपरासी, जल भरिया ( पानी के रखवाला) आ मंदिर के पहरा देवे वाला लोग मिल के करे ला। बद्रीनाथ उत्तर भारत के कुछ मंदिरन में से एगो ह जवन दक्खिन में अधिका आम श्रौत परंपरा के प्राचीन तंत्र विधि के पालन करेला। <ref>{{Cite web|url=http://traveller.outlookindia.com/fulltravelogue.aspx?id=216|title=Badrinath|last=Outlook Traveller|publisher=Traveller.outlookindia.com|archive-url=https://web.archive.org/web/20131029201558/http://traveller.outlookindia.com/fulltravelogue.aspx?id=216|archive-date=29 October 2013|access-date=1 January 2014}}</ref><ref>{{Cite news|url=http://www.hinduonnet.com/thehindu/fr/2008/07/18/stories/2008071851520400.htm|title=Badrinath Temple|date=18 July 2005|work=[[The Hindu]]|access-date=1 January 2014|archive-url=https://archive.today/20121111030256/http://www.hinduonnet.com/thehindu/fr/2008/07/18/stories/2008071851520400.htm|archive-date=11 November 2012}}</ref>
2012 में मंदिर प्रशासन मंदिर में आवे वाला लोग खातिर टोकन सिस्टम शुरू कइलस। टैक्सी स्टैंड के तीन स्टॉल से दौरा के समय बतावे वाला टोकन दिहल जाला। मुख्य देवता के दर्शन करे खातिर हर भक्त के 10-20 सेकंड के समय दिहल जाला। मंदिर में प्रवेश खातिर पहचान के प्रमाण अनिवार्य बा। <ref>{{Cite web|url=http://www.badarikedar.org/content-badari.aspx?id=29|title=News about the temple|year=2006|publisher=Shri Badrinath - Shri Kedarnath Temples Committee|archive-url=https://web.archive.org/web/20131029195444/http://www.badarikedar.org/content-badari.aspx?id=29|archive-date=29 October 2013|access-date=1 January 2014}}</ref> मंदिर में [[ऋषिकेश]] से पहुँचल जा सकेला, जवन {{Convert|298|km|mi|abbr=on}} पर स्थित बा [[देवप्रयाग]], [[रुद्रप्रयाग]], [[कर्णप्रयाग]], [[नंदप्रयाग]], [[ज्योतिर्मठ]], [[बिष्णुप्रयाग|विष्णुप्रयाग]] आ देवदर्शिनी के रास्ता से दूर बा। [[केदारनाथ मंदिर]] से, आगंतुक {{Convert|243|km|mi|abbr=on|adj=on}} के जात्रा कर सकेलें -लंबा रुद्रप्रयाग मार्ग या {{Convert|230|km|mi|abbr=on|adj=on}} के बा -लंबा उखीमठ अउर गोपेश्वर मार्ग।
== इहो देखल जाव ==
* रामेश्वरम
* दिव्य देशम
* भारत में धार्मिक पर्यटन
== संदर्भ ==
{{Reflist|33em}}
=== ग्रंथसूची ===
== आगे पढ़ल जा सकेला ==
[[श्रेणी:हिंदू तीरथ]]
[[श्रेणी:उत्तराखंड]]
[[श्रेणी:चार धाम मंदिर]]
8iuqmxia65ybvh27xmljw9ju7um9s0o
ओपनएआई
0
88250
803329
766287
2026-07-29T16:21:18Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803329
wikitext
text/x-wiki
{{Infobox company|name=OpenAI|logo=OpenAI Logo.svg|caption=|website={{Official URL}}}}
'''ओपनएआई''' एगो आर्टिफिशियल इंटेलिजेंस (AI) रिसर्च लैबोरेटरी हवे जेह में लाभ खातिर निगम '''ओपनएआई एलपी''' आ एकर मूल कंपनी, गैर-लाभकारी '''ओपनएआई इंक शामिल बाड़ी सऽ।''' कंपनी एआई के क्षेत्र में शोध करेले, जवना के बतावल लक्ष्य बा कि दोस्ताना एआई के बढ़ावा दिहल अउरी विकसित कइल जाए, जवना से समग्र रूप से मानवता के फायदा होखे। एह संगठन के स्थापना 2015 के अंत में सैन फ्रांसिस्को में सैम आल्टमैन, [[एलन मस्क]], आ अउरी लोग द्वारा कइल गइल, जे लोग सामूहिक रूप से 1 अरब अमेरिकी डॉलर के वादा कइल। मस्क फरवरी 2018 में बोर्ड से इस्तीफा दे दिहलें बाकी दानकर्ता बनल रहलें। 2019 में ओपनएआई एलपी के [[माइक्रोसाफ्ट|माइक्रोसॉफ्ट]] आ मैथ्यू ब्राउन कंपनी के द्वारा 1 अरब {{US$}} के निवेश भइल। ओपनएआई के मुख्यालय मिशन डिस्ट्रिक्ट, सैन फ्रांसिस्को में पायनियर बिल्डिंग में बा।
==इतिहास==
दिसंबर 2015 में, सैम आल्टमैन, एलन मस्क, ग्रेग ब्रॉकमैन, रीड हॉफमैन, जेसिका लिविंगस्टन, पीटर थिल, अमेजन वेब सर्विसेज (AWS), इन्फोसिस, आ वाईसी रिसर्च ओपनएआई के गठन के घोषणा कइलें<ref>{{Cite web |date=2015-12-12 |title=Introducing OpenAI |url=https://openai.com/blog/introducing-openai/ |access-date=2022-12-23 |website=OpenAI |language=en}}</ref> आ {{USD}}1 अरब के खर्चा के वादा उठवलें लोग। संगठन के कहनाम रहे कि ऊ आपन पेटेंट आ रिसर्च के जनता खातिर खुला बना के अन्य संस्थान आ शोधकर्ता लोग के साथे "स्वतंत्र रूप से सहयोग" करी।<ref>{{cite web |title=Introducing OpenAI |url=https://blog.openai.com/introducing-openai/ |website=OpenAI Blog |date=December 12, 2015 |access-date=January 6, 2023 |archive-date=February 24, 2019 |archive-url=https://web.archive.org/web/20190224031626/https://blog.openai.com/introducing-openai/ |url-status=dead }}</ref> ओपनएआई के मुख्यालय सैन फ्रांसिस्को के मिशन डिस्ट्रिक्ट में पायनियर बिल्डिंग में बा।<ref name=":02">{{Cite news|url=https://gizmodo.com/elon-musks-neuralink-sought-to-open-an-animal-testing-f-1823167674|title=Elon Musk's Neuralink Sought to Open an Animal Testing Facility in San Francisco|last=Conger|first=Kate|work=Gizmodo|access-date=October 11, 2018|language=en-US}}</ref><ref name=technologyreview>{{Cite web|url=https://www.technologyreview.com/s/615181/ai-openai-moonshot-elon-musk-sam-altman-greg-brockman-messy-secretive-reality/|title=The messy, secretive reality behind OpenAI's bid to save the world|last=Hao|first=Karen|date=February 17, 2020|website=MIT Technology Review|language=en-US|access-date=March 9, 2020}}</ref>
{{clear}}
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:कंपनी]]
{{Company-stub}}
eb59rvg3f4185bqtjlz8zl0iz8i9ncy
भानुभक्त आचार्य
0
88343
803370
772055
2026-07-29T16:24:22Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803370
wikitext
text/x-wiki
{{Infobox person
| honorific_prefix = '''आदिकबि'''
| name = भानुभक्त आचार्य
| image = File:Bhanubhakta Acharya.jpg
| alt = नेपाली कबी के चित्र
| caption = कबी के एगो पोर्ट्रेट
| birth_date = {{Birth date|df=yes|1814|7|13}}
| birth_place = Chundi Ramgha, [[Tanahu District]], [[Kingdom of Nepal]]<br><small>({{Present-day|[[Nepal]]}})</small>
| death_date = {{Death year and age|1868|1814}}
| death_place = Setighat, [[Tanahun District|Tanahun, Kingdom of Nepal]]<br> <small>({{Present-day|Nepal}})</small>
| nationality = नेपाली
| occupation = [[कबितई|कबी]]
| era = भानुभक्त जुग
| notable_works = ''भानुभक्त रामायण'', ''घांसी''
| parents = {{Plainlist|
*धनंजय आचार्य (बाबूजी)
*धर्मावती आचार्य (महतारी)}}
}}
'''भानुभक्त आचार्य''' (1814—1868 ई.) (1871—1925 [[विक्रम संवत|बिसं]] ) नेपाली साहित्यकार, कवि आ अनुवादक रहलें। इनके [[नेपाली भाषा]] के पहिला कवि के रूप में ब्यापक रूप से मानल जाला, जेकरा खातिर इनके "आदिकबी" के उपाधि दिहल गइल बाटे।
महाकाव्य ''[[रामायण]]'' के पहिला बेर [[संस्कृत]] से नेपाली में अनुवाद करे खातिर इनके जानल जाला। शुरू में उनकर रामायण के अनुवाद मुँहजबानी रूप में लोकप्रिय भइल। बाद में एकर संकलन आ प्रकाशन मोतीराम भट्ट द्वारा 19वीं सदी के अंत में ''[[भानुभक्त रामायण]]'' के रूप में कइल गइल।
== शुरुआती जिनगी आ शिक्षा ==
भानुभक्त आचार्य के जनम 13 जुलाई 1814 (29 आषाढ़ 1871 ईसा पूर्व) के नेपाल के [[विक्रम संवत|तनहुन]] [[तनहुँ जिला|जिला]] के चुंडी रामघा गाँव में धनंजय आचार्य, आ धर्मवती आचार्य के घरे भइल रहे।<ref>{{Cite web|url=https://english.onlinekhabar.com/bhanu-bhakta-acharya-nepal-aadikavi.html|title=Bhanu Bhakta Acharya: 5 reasons why a poet became a household name in Nepal|date=13 July 2021|website=OnlineKhabar|language=en-GB|archive-url=https://web.archive.org/web/20210713043312/https://english.onlinekhabar.com/bhanu-bhakta-acharya-nepal-aadikavi.html|archive-date=13 July 2021|access-date=2021-07-20}}</ref> उनकर पिता धनंजय आचार्य सरकारी अधिकारी रहले अउरी सभ भाई में सबसे बड़ रहले। भानुभक्त के प्राथमिक शिक्षा [[संस्कृत]] के अपना घर में दादाजी से आ बाद में [[बनारस|वाराणसी]] में भइल।<ref name=":0">{{Cite web|url=https://annapurnapost.com/news/kin-smjhne-bhaanulaaii-161113|title=किन सम्झने भानुलाई|last=आचार्य|first=काशीराज|date=2020-07-13|website=[[The Annapurna Express|The Annapurna Post]]|language=ne|access-date=2022-07-28}}</ref><ref name="boss">{{Cite web|url=http://bossnepal.com/adikabi-bhanubhakta-acharya|title=Adikabi Bhanubhakta Acharya|last=<!--Staff writer(s); no by-line.-->|publisher=Boss Nepal|location=Kathmandu|language=en|archive-url=https://web.archive.org/web/20190222151947/http://bossnepal.com/adikabi-bhanubhakta-acharya/|archive-date=22 February 2019|access-date=22 February 2019}}</ref>
== साहित्यिक कैरियर ==
नेपाली भाषा सहित दक्खिन एशियाई भाषा सभ ओह समय में अधिकतर भाषा प्रसार के मौखिक माध्यम ले सीमित रहलीं जिनहन के लिखित संदर्भ आ साहित्यिक परभाव बहुत कम रहे। चूंकि [[दक्खिन एशिया]] के अधिकतर लिखित ग्रंथन में संस्कृत के बोलबाला रहे एहसे ई अधिकतर आम जनता खातिर दुर्गम रहे। चूंकि ब्राह्मण लोग गुरु, विद्वान आ पुरोहित के रूप में उत्कृष्टता हासिल करे वाला जाति रहे एहसे धार्मिक शास्त्र आ अउरी साहित्यिक रचना सभ के पहुँच खाली ओह लोग तक सीमित रहे आ कुछे लोग अइसनो रहे जे शिक्षा पा सकत रहे आ संस्कृत के भी समझ सकत रहे। कई गो कवि लोग संस्कृत में कविता लिखले रहे जबकि आचार्य नेपाली भाषा में लिखे लगले जवना से खाली भर भाषा के लोकप्रियता ना मिलल बलुक उनुके राणा शासक लोग के भी स्वीकृति मिलल।
राम के वीर कारनामा के प्रति आचार्य के परोपकार उनका में आपन कथा के नेपाली बोले वाला लोग तक सुलभ बनावे के तात्कालिकता लेके आइल। चूँकि अधिकतर लोग संस्कृत भाषा ना समुझत रहे एहसे ऊ महाकाव्य के नेपाली भाषा में अनुवाद कइलें। ''रामायण'' के गीतात्मक कथ्य शैली के संरक्षित करत उनकर अनुवाद विद्वान लोग के मानना बा कि उहे गीतात्मक सार "भाव आ ''मरम" के वाहक बा जवन कविता नियन ना लागे के बजाय क्षेत्रीय प्रभाव के विकृत कइले बिना गीत के रूप में जादा सुनाई देलस'' रामायण ''के भीतरी अर्थ के बिना बिगड़ले।<ref name=":0" />
उहाँ के कवनो पाश्चात्य शिक्षा ना मिलल आ ना ही विदेशी साहित्य से परिचित रहले जवना से उहाँ के रचना आ अनुभव यात्रा के लोकभाषा साहित्यिक व्यवस्था के मौलिक बना के रखलस आ उहाँ के रचना में मजबूत नेपाली सवाद ले आइल। उनकर लेखन के प्रमुख विशेषता सरल रहे बाकिर मजबूत रहे जवना में धर्म के भाव, सादगी के भाव आ देश के गर्मजोशी रहे जवना के तुलना अउरी कई गो कवि ना कर पवले रहल। एगो अमीर परिवार के होखला के नाते उनुका कबो कवनो आर्थिक परेशानी ना भइल अउरी उनुकर जीवन तब तक अनोखा रहे, जब तक कि उनुकर मुलाकात एगो घास काटे वाला से ना भइल, जवन कि समाज के कुछ देवे के चाहत रहे ताकि उनुका के मरला के बाद भी याद कइल जा सके। घास काटे वाला के बात ही उनका के अइसन काम करे के प्रेरणा दिहलस जवन समाज पर आपन छाप छोड़ें।
उ अपना जीवन में दुगो कृति लिखले जवना में से एगो ''भानुभक्तेय रामायण'' अउरी दूसरा जेल में रहत छंद के रूप में लिखल प्रधानमंत्री के चिट्ठी ह।<ref>{{Cite web|url=https://beta.gorkhapatraonline.com/featured/2021-07-10-41602|title=भानुभक्तीय रामायण : दृष्टिविहीनलाई पनि|last=आचार्य|first=जयराज|date=2021-07-10|website=[[Gorkhapatra]]|language=ne|access-date=2022-07-28|archive-date=2022-07-28|archive-url=https://web.archive.org/web/20220728181652/https://beta.gorkhapatraonline.com/featured/2021-07-10-41602|url-status=dead}}</ref> कागज प हस्ताक्षर करे में कुछ गलतफहमी के चलते उनुका के बलि के बकरा बना के जेल भेज दिहल गइल। जेल में उनकर तबियत बिगड़ गइल आ उनका के मुक्त होखे के झूठा उम्मीद दिहल गइल बाकिर उनकर केस के सुनवाई तक ना भइल। त, उ प्रधानमंत्री के एगो याचिका लिख के आपन आजादी के निहोरा कइले, जवन बाद में उनुकर एगो महान रचना बन गइल। उनुका कविता से ना खाली आजादी जीतल गइल बलुक पइसा के झोरा भी दिहल गइल (उहाँ के ओही भाषा में लिखले रहले जवना के तब के प्रधानमंत्री जनता के जबरन इस्तेमाल करे के चाहत रहलें)। 1868 में जब उनकर निधन भइल त उनका मालूम ना रहे कि ऊ एक दिन नेपाल के सबसे पूज्य कवि में से एक हो जइहें। बाकिर उनकर रचना प्रकाशित ना भइल आ उनकर योगदान के श्रेय बिना मिलले उनकर निधन हो गइल।
उनकर रचना मोतीराम भट्ट द्वारा 1887 में प्रकाशित भइल जब ऊ पाण्डुलिपि के खोज के भारत के [[बनारस]], छपाई खातिर ले गइलें। आचार्य के एगो रचना काठमांडू घाटी आ ओकरा निवासी लोग के रंगीन, चमकदार प्रशंसा खातिर जानल जाला। भले ऊ नेपाल के सबसे मशहूर आ पूज्य कवि लोग में से एक हउवें बाकिर नेपाली साहित्य के इतिहास में उहाँ के रचना ओतना परसिद्ध नइखे जतना कि आन कवि लोग। रामायण, जेकर मूल रूप से आचार्य नेपाली में अनुवाद कइले रहलें, एक सदी से ढेर समय बाद अंग्रेजी में अनुवाद भइल; एकरा के अंग्रेजी के पहिला अनुवाद मानल जाला जवन नेपाल में प्रकाशित भइल।<ref>{{Cite web|url=https://risingnepaldaily.com/news/13638|title=A Nepali Ramayana For English Readers|last=Adhikary|first=Dhruba H.|date=2022-07-08|website=[[The Rising Nepal]]|publisher=[[Gorkhapatra Corporation]]|archive-url=https://web.archive.org/web/20220709163757/https://risingnepaldaily.com/news/13638|archive-date=2022-07-09|access-date=2022-07-29}}</ref>
== उल्लेखनीय रचना ==
=== महाकाव्य ===
* ''भानुभक्त रामायण''
=== कविता ===
* ''अमरावती कान्तिपुरी नगरी''
* ''गांसी <ref>{{Cite web|url=https://www.adb.org/sites/default/files/project-documents/52097/52097-002-iee-en.pdf|title=NEP: SASEC Mugling–Pokhara Highway Improvement Phase 1 Project|website=Asian Development Bank|page=44|archive-url=https://web.archive.org/web/20200720131153/https://www.adb.org/sites/default/files/project-documents/52097/52097-002-iee-en.pdf|archive-date=20 July 2020|access-date=20 July 2020}}</ref>''
* ''बढ़ू शिक्षा''
* ''भक्त माला''
* ''खूमित हाँ गिरधारी ले''
* ''रोज रोज दर्शन पौंछू (भोली कबीता)''
* ''मा भानुभक्त''
* ''बालाजी देखियन''
* ''प्रश्नोत्तर माला''
[[चित्र:Bhanu_Bhakta_Acharya_(1814-1869)_author.jpg|alt=Post stamp issued in Acharya's honour|thumb|319x319px| आचार्य के सम्मान में जारी डाक टिकट]]
== विरासत ==
भानुभक्त आचार्य के नेपाली भाषा के ''आदिकबी'' (पहिल कवि) के उपाधि से पूज्य आ सम्मानित कइल जाला। मोतीराम भट्ट, पहिला बेर 1981 में आचार्य के जीवनी लिखत घरी उनुका के आदिकवी के नाम से संबोधित कइले रहलें। ऊ साफ करत बाड़न कि आचार्य के ''आदिकवि'' ना कहल जाला काहे कि ऊ नेपाल के पहिला कवि रहले बाकिर ऊ उपाधि के हकदार रहले काहे कि ऊ पहिला कवि रहले जे कविता के ''मरम'' (अंतर्सार) के समझ के लिखले रहले। <ref name="boss" /><ref name="OMN">{{Cite web|url=http://english.ohmynews.com/articleview/article_view.asp?no=304833&rel_no=1|title=Bhanubhakta: The First Poet Of Nepali language|last=Bishnu K.C.|date=14 July 2006|publisher=Oh My Newsl|language=en|archive-url=https://web.archive.org/web/20191106025354/http://english.ohmynews.com/articleview/article_view.asp?no=304833&rel_no=1|archive-date=6 November 2019|access-date=22 February 2019}}</ref>
'''भानु जयंती''' भानुभक्त आचार्य के जयंती के उत्सव ह। नेपाली कैलेंडर के अनुसार ई आषाढ़ महीना के 29 तारीख के पड़ेला। एकरा के हर साल नेपाल सरकार आ नेपाली लोग के साथे-साथे दुनिया भर के [[नेपाली भाषा|नेपाली]] भाषी लोग द्वारा मनावल जाला। सांस्कृतिक उत्सव, '''भानुभक्त आचार्य के''' जन्मदिन के याद में दुनिया भर के [[नेपाल|नेपाली]] लोग के बीच प्रचलित बाटे। <ref>{{Cite web|url=https://morungexpress.com/why-do-gorkhas-celebrate-bhanu-jayanti-as-their-cultural-festival|title=Why do Gorkhas celebrate Bhanu Jayanti as their Cultural Festival?|last=Sundas|first=Jacob|date=2022-07-07|website=[[The Morung Express]]|archive-url=https://web.archive.org/web/20220709190808/https://morungexpress.com/why-do-gorkhas-celebrate-bhanu-jayanti-as-their-cultural-festival|archive-date=2022-07-09|access-date=2022-07-09}}</ref> आमतौर पर ई 13 जुलाई या नेपाली आषाढ़ महीना के 29 तारीख के मनावल जाला।
हर साल भानु जयंती साहित्यिक गोष्ठी, आ कार्यक्रम के साथे आ नेपाली लेखक, उपन्यासकार, आ अउरी साहित्यिक हस्ती/उत्साही लोग के उल्लेखनीय उपस्थिति के बीच एगो मेगा इवेंट के रूप में मनावल जाला। <ref name="boss" /><ref>{{Cite news|url=https://thehimalayantimes.com/kathmandu/pm-oli-urges-new-generation-to-follow-ideals-of-bhanubhakta|title=PM Oli urges new generation to follow ideals of Bhanubhakta|last=<!--Staff writer(s); no by-line.-->|date=13 July 2018|access-date=21 February 2019|archive-url=https://web.archive.org/web/20190222041943/https://thehimalayantimes.com/kathmandu/pm-oli-urges-new-generation-to-follow-ideals-of-bhanubhakta/|archive-date=22 February 2019|location=Kathmandu|language=en}}</ref><ref>{{Cite news|url=https://kathmandupost.ekantipur.com/news/2018-07-13/morning-procession-to-mark-bhanu-jayanti-in-photos.html|title=Morning procession to mark Bhanu Jayanti|last=<!--Staff writer(s); no by-line.-->|date=13 July 2018|access-date=21 February 2019|archive-url=https://web.archive.org/web/20190221224020/https://kathmandupost.ekantipur.com/news/2018-07-13/morning-procession-to-mark-bhanu-jayanti-in-photos.html|archive-date=21 February 2019|location=Kathmandu|language=en}}</ref>
== गैलरी ==
<gallery>
चित्र:Statue of Bhanubhakta Acharya at Chundi Ramgha 01.jpg|चुंडी रामघा में भानुभक्त आचार्य की प्रतिमा
चित्र:Bhanu salik Nepal Academy5.JPG|[[Nepal Academy|नेपाल अकादमी]] में भानुभक्त की प्रतिमा
चित्र:Aadikavi Bhanubhakta Acharya.jpg|भानुभक्त आचार्य के बस्ट
चित्र:Bhanubhakta letter.jpg|भानुभक्त आचार्य के पुत्र के पत्र (1858)
चित्र:Bhanubhakta statue.JPG|[[Chowrasta (Darjeeling)|चौरास्ता]], [[Darjeeling|दार्जिलिंग]] में भानुभक्त आचार्य की प्रतिमा
चित्र:Bhanu Ghansi Memorial ParkTanahu Ward No. 12 Byas Municipility Gandaki Pradesh Nepal Rajesh Dhungana (28).jpg|alt=भानु घंसी मेमोरियल पार्क, तनहुन में भानुभक्त का बस्ट|भानु घंसी मेमोरियल पार्क, [[Tanahun District|तनहुन]] में भानुभक्त का बस्ट
</gallery>
== इहो देखल जाव ==
* गांसी, जे भानुभक्त आचार्य के संस्कृत महाकाव्य ''रामायण'' के अनुवाद करे खातिर प्रेरित कइले रहले
* शक्ति बल्लव अर्यल
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:नेपाली-भाषा के कबी]]
[[श्रेणी:हिंदू कबी]]
k841dmd07ayjxexxxkwnhoz5lgzfogg
लाल बहादुर शास्त्री राष्ट्रीय प्रशासनिक प्रशिक्षणदेयी संस्था
0
88384
803355
784047
2026-07-29T16:23:09Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803355
wikitext
text/x-wiki
{{Cleanup|reason=फ़ॉर्मेटिंग में कई सारा समस्या, फालतू बोल्ड पाठ वगैरह बाटे}}
{{Infobox university
|name=Lal Bahadur Shastri National Academy of Administration
|native_name=
|native_name_lang=
|image=Lbsnaa_academy_logo.jpg
|image_size=150px
|former_name=राष्ट्रीय प्रशासनिक संस्था
|motto= "शीलं परं भूषणम्" ([[संस्कृत| संस्कृतम्]])<br> "चरीत्र सभसे बड़ गहना बा"
|motto_lang=san
|motto in bh="चरित्र से बड़का गहना कुछो नइखे"
|established=1958
|type=[[लोक सेवा]] प्रशिक्षण संस्थान
|director=श्रीनिवास कटीकीतला, [[भारतीय प्रशासनिक सेवा|IAS]]
|city=[[शिमला]] आ [[न्यू दिल्ली]] 1972 में; [[मसूरी]], [[उत्तराखण्ड]] वर्तमान में
|country=भारत
|campus=शहरी
|website={{URL|http://www.lbsnaa.gov.in}}
|logo=
|parent=[[जनोरहन आ पेंशन मंत्रालय]]
}}
'''लाल बहादुर शास्त्री राष्ट्रीय प्रशासनिक प्रशिक्षणदेयी संस्था''' [[भारत|भारतऽगणराज्य]] में [[लोकनीति]] आ [[लोक प्रशासन]] पर सिविल सेवा प्रशिक्षण देवनीहार संस्थान बा।<ref>{{cite web |url=https://byjus.com/free-ias-prep/lbsnaa-history-and-some-interesting-facts/}}</ref> संस्था के मुख्य मकसद आई.ए.एस सेवादेयी सदस्यन के प्रशिक्षित कइल आ ग्रुप-ए केन्द्रीय सिविल सेवा के फाउंडेशन कोर्स के संचालनो करऽल बा। प्रशिक्षण पूरा होखला के बाद आई.ए.एस सेवादेयी सदस्यन के प्रशिक्षु अधिकारीयन के [[जवाहरलाल नेहरू विश्वविद्यालय]], [[दिल्ली]] से ए.मए (लोक प्रशासन) से सम्मानित कइल जाला। ई 1985 से [[जनोरहन आ पेंशन मंत्रालय]] के तहत काम करत बा।
एल.बी.एस.एन.ए.ए.<ref>{{cite web |url=https://www.eliteias.in/everything-about-lbsnaa/}}</ref> [[उत्तराखंड|उत्तराखण्ड]] के [[मसूरी]] के सुन्दरं पर्वत में स्थित एगो प्रमुख प्रशिक्षणदेयी संस्था बा।1958 में स्थापित भइल एह प्रशिक्षणदेयी संस्था में हर बरिस सिविल सेवकन के एगो नया बेच लौकेला। प्रशिक्षणदेयी संस्था [[अखिल भारतीय सेवा]] में प्रवेश कर्नीहार लोगीन खातिर एगो सामान्य फाउंडेशन कोर्स चलावेले। अक्टूबर 1972 में प्रशिक्षणदेयी संस्था के नांव बदल के '''"लाल बहादुर शास्त्री अकादमी ऑफ एडमिनिस्ट्रेशन"'''<ref>{{cite web|url=https://iasbabuji.com/upsc-training/lbsnaa/|title=आर्काइव कॉपी के बा|access-date=2023-01-22|archive-date=2023-01-22|archive-url=https://web.archive.org/web/20230122053458/https://iasbabuji.com/upsc-training/lbsnaa/|url-status=dead}}</ref> कर दिहल गइल आ जुलाई 1973 में एहमें राष्ट्रीय शब्द जोड़ल गइल। अब प्रशिक्षण संस्था के "'''लाल बहादुर शास्त्री नेशनल एकेडमी ऑफ एडमिनिस्ट्रेशन" (LBSNAA)''' के नांव से चिन्हल जाला। जेकरा के [[भोजपुरी]] मे '''"लाल बहादुर शास्त्री राष्ट्रीय प्रशासनिक प्रशिक्षणदेयी संस्था"''' कहल जाला। अप्रिल 1985 से ई जनोरहन आ पेंशन मंत्रालय के तहत काम करत बा।
== परिचय ==
[[चित्र:Lal_Bahadur_Shastri_National_Academy_of_Administration,_Mussoorie,_at_night.JPG|alt=Lal Bahadur Shastri National Academy of Administration, Mussoorie, at night|thumb|लाल बहादुर शास्त्री राष्ट्रीय प्रशासनिक प्रशिक्षणदेयी संस्था, राती में]]
15 अप्रिल 1958 के तत्कालीन केंद्रीय गृहमंत्री [[पण्डित गोविन्द बल्लभ पन्त]] [[लोकसभा]] में घोषणा कइले के [[भारत सरकार|सरकार]] एगो नेशनल एकेडमी ऑफ एडमिनिस्ट्रेशन के स्थापना करी, जहवां सिविल सेवा में भर्ती होखनिहारन के प्रशिक्षण दिहल जाई। [[घर मंत्रालय, भारत|गृह मंत्रालय]] दिल्ली के आई.ए.एस ट्रेनिंग स्कूल आ [[शिमला]] के आई.ए.एस स्टाफ कॉलेज के मिला के 1 सितंबर 1959 में [[मसूरी]] में नेशनल एकेडमी ऑफ एडमिनिस्ट्रेशन के स्थापित करऽल गइल। प्रशिक्षणदेयी संस्था दु बेरा आपन नांव में बदलाव कइलस। अक्टूबर 1972 में एकर नाम बदल के '''"लाल बहादुर शास्त्री एकेडमी ऑफ एडमिनिस्ट्रेशन"''' रखल गइल। जुलाई 1973 में एह संस्था के वर्तमान नाँव '''"लाल बहादुर शास्त्री नेशनल एकेडमी ऑफ एडमिनिस्ट्रेशन (LBSNAA)"''' धरल गइल।
अधिकारीयन के पहिला बॅच के प्रशिक्षण 13 अप्रैल 1959 में 115 अधिकारीयन के सङे मेटकाफ हाउस, दिल्ली में शुरू भइल। 1 सितंबर 1959 में प्रशिक्षणदेयी संस्था मसूरी के चार्लेविले होटल में स्थानफेर हो गइल आ वर्तमान में एहिजा स्थिर बा। मसूरी में प्रशिक्षणदेयी संस्था द्वारा शुरू कइल गइल चार्लेविले होटल एह [[हिल स्टेशन]] में बनल पहिला होटल रहल। एकरा के सरकार निजी व्यक्ति से किन्ले रहे। एह होटल के मुख्य भवन 1854 में जनरल विल्किंसन द्वारा बनवावल गइल रहे। एकरा के 1861 में मसूरी धननिधिकोष (बैंक) के रिटायर्ड मैनेजर मिस्टर हॉब्सन किन्ले रहले। मशहूर लेखक [[रडयार्ड किपलिंग|रुडयार्ड किपलिंग]] 1888 में इहावा रहऽत रहले। 1905 में यूके के रानी मैरी (ओह घरी प्रिंसेस ऑफ वेल्स के नाम से जानल जाली) एह होटल में रुकल रहली।
31 अगस्त 1970 के गृह मंत्रालय के तहत प्रशिक्षणदेयी संस्था के कामकाज शुरू भइल। 1 सितंबर 1970 से अप्रैल 1977 ले ई संस्था [[कैबिनेट सचिव मामिला मंत्रालय]] के नियंत्रण में रहल, अप्रैल 1977 से मार्च 1985 ले फेनु से [[घर मंत्रालय, भारत|गृह मंत्रालय]] के नियंत्रण में आगई आ अप्रैल 1985 से आजले [[जनोरहन आ पेंशन मंत्रालय]] के नियंत्रण मे काम करता। 1984 में एगो भयानक आग लगला से मुख्य होटल के भवन तबाह हो गइल रहे। एन.आई.सी.टी.यू (NICTU) के स्थापना 1988 में भइल रहे। 1989 में "नेशनल सोसाइटी फॉर प्रोमोशन ऑफ डेवलपमेंट एडमिनिस्ट्रेशन रिसर्च एंड ट्रेनिंग (NSDART)" के स्थापना भइल। 14 नवंबर 1996 से एकरा के "नेशनल इंस्टीट्यूट ऑफ एडमिनिस्ट्रेशन रिसर्च" (NIAR)के नाम से जानल जाला। 3 नवंबर 1992 में प्रशिक्षणदेयी संस्था के कर्मशिला भवन आ 9 अगस्त 1996 ध्रुवशिला आ कालिंदी अतिथि गृह के उद्घाटन करऽल गइल।2011 में भवन ज्ञानशिला, सिल्वरवुड आ वैली व्यू के निर्माण भइल। 21 जून 2013 ब्रह्मपुत्र भवन आ 29 जून 2015 आधारशिला भवन के उद्घाटन भइल।
[[चित्र:The_Prime_Minister,_Shri_Narendra_Modi_in_a_group_photograph_with_the_Officer_Trainees_of_the_92nd_Foundation_Course,_at_the_Lal_Bahadur_Shastri_National_Academy_of_Administration_(LBSNAA),_in_Mussoorie,_Uttarakhand_(1).jpg|alt=The Prime Minister, Shri Narendra Modi in a group photograph with the Officer Trainees of the 92nd Foundation Course, at the Lal Bahadur Shastri National Academy of Administration (LBSNAA), in Mussoorie, Uttarakhand (1)|thumb|संस्था के एगो कार्यक्रम में प्रधानमंत्री मोदी]]
भारत मे देश के प्रमुख सिविल सेवा में ढेरका अधिकारीयन के पसंदगी [[संघ लोक सेवा आयोग]] (UPSC) के ओर से आयोजित सिविल सेवा परीक्षा के माध्यम से होखेला। पसंद कएल उम्मीदवार चार महीना के फाउंडेशन कोर्स खातिर लाल बहादुर शास्त्री राष्ट्रीय प्रशासनिक प्रशिक्षणदेयी संस्था में शामिल होलें आ एह कोर्स में सगरी प्रशिक्षु लोग में "समानता" के भाव पन्कावल जाला.
एकरा बाद [[भारतीय प्रशासनिक सेवा]] (IAS)के अधिकारी अकादमी में आपन व्यवसायिक अभ्यास प्रशिक्षण जारी रखेले,<ref name="byjus.com">{{cite web |url=https://byjus.com/free-ias-prep/lbsnaa-ias-training-bharat-darshan/ |website=byjus}}</ref> जबके दोसर सेवा के अधिकारी, माने [[भारतीय विदेश सेवा]] (IFS) के अधिकारी खातीर न्यू दिल्ली के "[[सुषमा स्वराज विदेश सेवा संस्थान]]" आ [[भारतीय पुलिस सेवा]] (IPS) के अधिकारीयन खातिर [[हैदराबाद]] में [[सरदार वल्लभभाई पटेल राष्ट्रीय पुलिस प्रशिक्षणदेयी संस्था]], आ [[भारतीय वन सेवा]] (IFS) के अधिकारी खातिर [[इंदिरा गांधी राष्ट्रीय वन प्रशिक्षणदेयी संस्था]], [[देहरादून]] आ भारतीय राजस्व सेवा (IRS) के अधिकारीयन खातिर "नेशनल एकेडमी ऑफ कस्टम इन्डायरेक्ट टैक्स एंड नारकोटिक्स" बाटे।
एकरा बाद [[भारतीय प्रशासनिक सेवा]] (IAS)के अधिकारी अकादमी में आपन व्यवसायिक अभ्यास प्रशिक्षण जारी रखेले,<ref name="byjus.com"/> जबके दोसर सेवा के अधिकारी, माने [[भारतीय विदेश सेवा]] (IFS) के अधिकारी खातीर न्यू दिल्ली के "[[सुषमा स्वराज विदेश सेवा संस्थान]]" आ [[भारतीय पुलिस सेवा]] (IPS) के अधिकारीयन खातिर [[हैदराबाद]] में [[सरदार वल्लभभाई पटेल राष्ट्रीय पुलिस प्रशिक्षणदेयी संस्था]], आ [[भारतीय वन सेवा]] (IFS) के अधिकारी खातिर [[इंदिरा गांधी राष्ट्रीय वन प्रशिक्षणदेयी संस्था]], [[देहरादून]] आ भारतीय राजस्व सेवा (IRS) के अधिकारीयन खातिर "नेशनल एकेडमी ऑफ कस्टम इन्डायरेक्ट टैक्स एंड नारकोटिक्स" बाटे।
प्रशिक्षणदेयी संस्था 2007 में भारतीय प्रशासनिक सेवा के अधिकारीयन खातिर मिड कैरियर ट्रेनिंग प्रोग्रामो चलावल शुरू कइलस। अंदाजे 15 साल के सेवा वाला अधिकारी जे [[संयुक्त सचिव]] बने वाला बाड़ें, चौथा चरण मिड कैरियर ट्रेनिंग प्रोग्राम से गुजरेलें, जबके लगभग आठ साल के सेवा वाला अधिकारी सेवा के तृतीय चरण के मध्य कैरियर प्रशिक्षण कार्यक्रम से गुजरेले। प्रशिक्षण खातिर सबसे व्यापक ज्ञान के आधार उपलब्ध करावे खातिर अकादमी दुनिया के मशहूर थिंक टैंक आ आइवी लीग रिसर्च इंस्टीट्यूशन के जवड़े काम कर रहल बा। [[गूगल]] आ सिद्धार्थ पॉल तिवारी के सङे साझेदारी के साथे प्रशिक्षणदेयी संस्था नवीनतम् तकनीक के प्रशिक्षण शुरू कइलक एह से प्रशिक्षुवन एह क्षेत्र में भइल सभसे ताजा नवाचार सभ के बारे में जानकारी ले सकेले। प्रशिक्षणदेयी संस्था शासन आ लोक प्रशासन के बिबिध पासा में केतनहे नन्हिचुकी अवधि के प्रशिक्षण कार्यक्रमो चलावे ले।
==सुविधा==
प्रशिक्षणदेयी संस्था में सेवादेयी सदस्यन के विशाल आ सुसज्जित लेक्चर हॉल, ग्रन्थालय, सभाखण्ड, ऑडिटोरियम, मेस, निवासस्थान, खेल परिसर, मेडिकल सुविधा, रिप्रोग्राफिक यूनिट, ई.पी.ए.बी.एक्स (EPABX), [[कंप्यूटर]] लॅब, बैंक, डाकघर आ [[इंटरनेट]] के सुविधा मिली।
=== शिक्षण सुविधा ===
प्रशिक्षणदेयी संस्था में नवीनतम् ऑडियो-विजुअल सुविधा वाला विशाल आ सुसज्जित कक्षा के साथे-साथे वीडियो-रिकॉर्डिंग आ वीडियो-कांफ्रेंसिंग के सुविधा उपलब्ध बा। प्रशिक्षणदेयी संस्था में चारगो मुख्य भवन बा; '''कर्मशिला''', '''ध्रुवशिला''', '''ज्ञानशिला''' आ '''आधारशिला'''। कर्मशिला में लेक्चर हॉल, अधिकारीयन के मेस, ग्रन्थालय आ अधिकारीयन आरामस्थानो बा। फेकल्टी कार्यालय आ प्रशासनिक कार्यालय ध्रुवशिला आ ज्ञानशिला में बा। ज्ञानशिला में तनी-बहुत कक्षो बा। आधारशिला में कंप्यूटर लॅब आ तनी-बहुत कक्षो बा।
=== निवासस्थान सुविधा ===
प्रशिक्षण के बेरीया अधिकारी प्रशिक्षुवन के प्रशिक्षणदेयी संस्था में रहे के मनसा करल जाला। प्रशिक्षुवन खातिर तीनगो हॉस्टल बाटे जेकर के नाम [[भारत]] के तीनगो बड़ [[नदी|नद्दीयन]] के नाम पर धरल गइल बाटे [[गंगा नदी|गंगा]], [[कावेरी नदी|कावेरी]] आ [[नर्मदा नदी|नर्मदा]]। प्रशिक्षणदेयी संस्था हॉस्टल के सभे कमरा में [[केंद्रीय तापन-प्रणाली]], [[टेलीविजन]], [[टेलीफोन]] बा आ डबल ओक्यूपेंसी के सुविधा बा। मिड कैरियर ट्रेनिंग प्रोग्राम के प्रतिभागियन के सिल्वरवुड, हेप्पी वैली, [[महानदी]] आ वैली व्यू एक्जीक्यूटिव हॉस्टल में राखल गइल बा। सभे हॉस्टल में आरामस्थान, ऑडियो-विजुअल उपकरण, आधुनिक लॉन्ड्रोमैट आ आम [[मनोरंजन]] क्षेत्र बा। विजिटिंग फैकल्टी कालिंदी अतिथि गृह में बाटे। प्रशिक्षणदेयी संस्था के ब्रह्मपुत्र भवन में साङ्घतीया आ सहायक फैकल्टी खातिर स्टूडियो अपार्टमेंट बाटे।
=== खेला परिसर ===
लाल बहादुर शास्त्री राष्ट्रीय प्रशासनिक प्रशिक्षणदेयी संस्था में एगो अद्भुत खेल परिसर बा जवना में एथलेटिक्स, [[फुटबाल|फुटबॉल]], [[क्रिकेट]], [[वॉलीबॉल]], [[स्क्वैश]], [[टेनिस]], [[बैडमिंटन]], [[योग]], [[बास्केटबॉल]], [[बिलियर्ड्स]], [[टेबल टेनिस]] आदि के सुविधा बा। एह में आधुनिक जिम बा। एकरा अलावे प्रशिक्षु लोग के [[घोड़सवारी]], [[शूटिंग]], [[तीरंदाजी]], [[आत्मरक्षा]], [[पर्वतारोहण]] आदि के प्रशिक्षण भी दिहल जाला। अधिकारी प्रशिक्षु लोग के पर्वतारोहण ट्रेकिंग, रिवर राफ्टिंग, पैरासेलिंग आ बाकी एडवेंचर [[खेल]] खातीरो ले जाइल जाला।
=== ग्रन्थालय ===
[[गांधी स्मृति ग्रन्थालय]] के नाँव से चिन्हल जाए वाल एह ग्रन्थालय के लगे 1.65 लाख से ढेर [[किताब]], 5000 सीडी/डीवीडी, 300 गो पत्र आ पत्रिका आ 38 गो [[अखबार]] बाटे। फोटोकॉपी के सुविधो उपलब्ध बा।
=== औषधालय ===
प्रशिक्षणदेयी संस्था में डॉक्टर आ नर्सन के [[डाक्टरी (बिद्या)|मेडिकल]] केन्द्रो बा। एकरा अलावे एगो [[पेथोलॉजी]] प्रयोगशाला , एक्स-रे यूनिट, डेंटल अउरी [[फिजियोथेरेपी]] यूनिटो बा।
=== आईटी सेवा ===
प्रशिक्षणदेयी संस्था के [[कंप्यूटर]] सेक्शन डाटा प्रोसेसिंग आ हिसाब लेखा में लागल बा। एकरा अलावे एकरा में हाई स्पीड [[इंटरनेट]] कनेक्शन अउरी एडवांस [[सॉफ्टवेयर]] पेकेज बा।
=== अधिकारीयन के मेस ===
अधिकारीयन के मेस प्रशिक्षणदेयी संस्था के भीतर एगो खान-पान के संस्था बा। परिवीक्षाधीन लोग द्वारा चलावल जाए वाला ई जगहा परिवीक्षाधीन लोग के बीच वैश्विक भाईचारा आ बन्धुत्ता के भावना उपजावे खातीर कोशिश करेले। एकर जिम्मेदारी औपचारिक आ अनौपचारिक मिलन समारोह के आयोजन करे के बा।
=== इंदिरा भवन परिसर ===
ई सुविधा मुख्य परिसर से एक किलोमीटर दूर बा आ कक्षा में पढ़ावे आ कार्यशाला/सम्मेलन खातिर एगो विषेश सुविधा बा।
[[लाल बहादुर शास्त्री]] राष्ट्रीय प्रशासनिक प्रशिक्षणदेयी संस्था में मौजूद सभे सुविधा में अलग-अलग सक्षम लोग प विचार शामिल बा। अकादमी के हर भवन में एक्सेस रैंप आ ब्रेल कोडित लिफ्ट लगावल गइल बा जवना के इस्तेमाल विकलांग व्यक्ति श्रेणी के अधिकारी प्रशिक्षु लोग करीहें। प्रशिक्षणदेयी संस्था अलग-अलग सक्षम प्रशिक्षु लोग के जरूरत के आधार पर एगो सहायक नियुक्त करेले ताकि रोजमर्रा के गतिविधि में मदद मिल सके। अलग-अलग क्षमता वाला लोग के उपयोग खातीर कैंपस के भीतर इलेक्ट्रिक रेक्सो बा। एकरा अलावे अलग-अलग क्षमता वाला सभ अधिकारी प्रशिक्षुवन के हेप्पी वैली हॉस्टल बिल्डिंग में ग्रन्थालय, ऑफिसर मेस आ कक्षा तक चहुपे के सुविधा दिहल जाला।
लाल बहादुर शास्त्री राष्ट्रीय प्रशासनिक प्रशिक्षणदेयी संस्था के जीवन शिक्षण, रोमांच आ मस्ती से भरल बा। ई एगो परिवीक्षाधीन [[भारतीय प्रशासनिक सेवा|आई.ए.एस]] के प्रशिक्षु अधिकारी में ढाल देला आ ओह लोग के आई.ए.एस अधिकारी के काम करे खातिर जरूरी कौशल से परिपूर्ण करेला।
== संदर्भ ==
{{Reflist|33em}}
qa8rt51l3ysenwz4hhf8wu913v6jyix
हवा क्वालिटी इंडेक्स
0
88639
803371
791990
2026-07-29T16:24:28Z
SM7Bot
7808
/* भारत */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803371
wikitext
text/x-wiki
[[चित्र:Sha1993_smog_wkpd.jpg|thumb| चीन के शंघाई में [[स्मॉग|धुंध]], दिसंबर, 1993 के एगो तस्वीर : आम तौर पर सेहत के नोकसान पहुँचावे वाला लेवल के रेटिंग कइल जाए वाली हवा के स्थिति के एगो उदाहरण बाटे।]]
[[चित्र:25August_2007_Greek_fires.jpg|thumb|एगो एनोटेटेड सैटेलाइट फोटो जवना में ग्रीस (यूनान) में जंगली आग के धुँआ देखावल गइल बा, जवना से हवा के एक्यूआई बढ़ल बा, मने की इस्थिति ख़राब भइल बाटे।]]
'''हवा क्वालिटी इंडेक्स''' ({{Lang|en|air quality index}} (संछेप में '''AQI'''), {{lang|hi|वायु गुणवत्ता सूचकांक}}) के इस्तेमाल सरकारी एजेंसी सभ<ref>{{Cite web|url=http://www.airnow.gov/index.cfm?action=airnow.international|title=International Air Quality|archive-url=https://web.archive.org/web/20180612162706/https://www.airnow.gov/index.cfm?action=airnow.international|archive-date=12 June 2018|access-date=20 August 2015}}</ref> जनता के बतावे खातिर करे लीं कि वर्तमान में [[हवा प्रदूषण|हवा केतना प्रदूषित]] बा चाहे फिर केतना प्रदूषित होखे के अनुमान लगावल गइल बा।<ref>{{Cite web|url=https://airquality.weather.gov/|title=NOAA's National Weather Service/Environmental Protection Agency - United States Air Quality Forecast Guidance|website=airquality.weather.gov|access-date=2021-04-28}}</ref><ref>{{Cite web|url=https://www.gmes-atmosphere.eu/services/raq/raq_nrt/|title=MACC Project - European Air Quality Monitoring and Forecasting|archive-url=https://web.archive.org/web/20141018213242/https://www.gmes-atmosphere.eu/services/raq/raq_nrt/|archive-date=2014-10-18|access-date=2014-10-12}}</ref> एक्यूआई के जानकारी हवा के क्वालिटी सेंसर से औसतन रीडिंग से मिल जाला, जवन गाड़ी के आवाजाही, जंगल में आग लागे, या कवनो अइसन चीज के कारण बढ़ सके ला जेह से [[हवा प्रदूषण]] बढ़ सके ला। टेस्ट कइल जाये वाला प्रदूषक में [[ओजोन]], [[नाइट्रोजन डाइऑक्साइड]], [[सल्फर डाइऑक्साइड]], आ अउरी कई गो पदार्थ शामिल बाड़ें।
एक्यूआई बढ़ला पर जनस्वास्थ्य के जोखिम बढ़ जाला, खासतौर पर बच्चा, बुजुर्ग आ साँस भा हृदय संबंधी समस्या वाला ब्यक्ति सभ के प्रभावित करे ला। एह समय सभ में सरकारी निकाय सभ आमतौर पर लोग हर शारीरिक गतिविधि कम करे खातिर प्रोत्साहित करे लीं, या फिर बाहर जाए से भी बिल्कुल परहेज करे के सलाह देवे लीं। कपड़ा के मास्क जइसन [[फेस मास्क]] के इस्तेमाल के भी सलाह दिहल जा सक'ता।
अलग-अलग देस सभ के आपन-आपन हवा के गुणवत्ता के सूचकांक बनावे के तरीका होला जे अलग-अलग राष्ट्रीय हवा के गुणवत्ता मानक सभ के अनुरूप होला। एह में से कुछ कनाडा के हवा गुणवत्ता स्वास्थ्य सूचकांक, मलेशिया के वायु प्रदूषण सूचकांक, आ सिंगापुर के प्रदूषक इस्टैंडर सूचकांक बाड़ें।
== संछेप में ==
[[चित्र:Edinburgh_air_quality_measurement_station_dsc06786.jpg|thumb| [[एडिनबर्ग]], [[स्कॉटलैंड]] में एगो हवा के गुणवत्ता माप स्टेशन]]
एक्यूआई के गणना खातिर एगो निर्धारित औसत समय के दौरान हवा प्रदूषक के कंसंट्रेशन के नापजोख के जरूरत होला, जवन एयर मॉनिटर भा मॉडल से हासिल कइल गइल होला। एकरा के मिला के देखल जाय तब कंसंट्रेशन आ समय मिल के हवा प्रदूषक के डोज के बतावे ला। कौनों दिहल गइल डोज के अनुरूप स्वास्थ्य परभाव सभ के स्थापना [[महामारी|महामारी बिज्ञान]] के रिसर्च से कइल जाला।<ref>{{Cite web|url=http://www.epa.gov/risk_assessment/dose-response.htm|title=Step 2 - Dose-Response Assessment|date=26 September 2013|access-date=20 August 2015}}</ref> हवा प्रदूषक सभ के ताकत अलग-अलग होला आ हवा प्रदूषक के कंसंट्रेशन से एक्यूआई में बदले खातिर इस्तेमाल होखे वाला कामकाज प्रदूषक के हिसाब से अलग-अलग होला। एकर हवा के क्वालिटी इंडेक्स मान सभ के आमतौर पर रेंज में समूहबद्ध कइल जाला। हर रेंज के डिस्क्रिप्टर, रंग कोड बतावल जाला आ ओकरा हिसाब से स्टैंडर्ड जनस्वास्थ्य सलाह दिहल जाला।
हवा के में प्रदूषक चीज छोड़े (एमिशन) में बढ़ोतरी के चलते एक्यूआई बढ़ सकता। उदाहरण खातिर, भीड़भाड़ के समय यातायात के दौरान या जब हवा के ऊपर जंगल में आग लागे ला या वायु प्रदूषक सभ के पतला होखे के कमी से। ठहरल हवा, अक्सर [[एंटीसाइक्लोन]], तापमान उलटफेर, या [[हवा]] के कम गति के कारण होखे वाली हवा के प्रदूषण के स्थानीय इलाका में रहे देला, जेकरा चलते प्रदूषक सभ के ढेर कंसंट्रेशन, हवा के दूषित पदार्थ सभ के बीच रासायनिक रिएक्शन आ धुंध के स्थिति पैदा हो जाले।<ref>{{Cite web|url=http://www.kjc.gov.my/english/education/weather/haze01.html|title=Haze|last=Myanmar government|date=2007|archive-url=https://web.archive.org/web/20070127214023/http://www.kjc.gov.my/english/education/weather/haze01.html|archive-date=27 January 2007|access-date=2007-02-11}}</ref>
[[चित्र:SignboardAirQualityHouston.JPG|thumb| गल्फटन, ह्यूस्टन में साइनबोर्ड जवन [[ओजोन]] घड़ी के संकेत देत बा]]
जवना दिन महीन कण प्रदूषण के कारण एक्यूआई के बढ़े के अनुमान लगावल जाला, कवनो एजेंसी भा जनस्वास्थ्य संगठन:
* संवेदनशील समूह, जइसे कि बुजुर्ग, बच्चा, आ श्वसन भा हृदय संबंधी समस्या वाला लोग के सलाह देवे लीं कि बाहरी मेहनत से बचे के चाहीं.<ref>{{Cite web|url=http://www.lung.org/healthy-air/outdoor/air-quality-index.html|title=Air Quality Index - American Lung Association|website=American Lung Association|archive-url=https://web.archive.org/web/20150828143922/http://www.lung.org/healthy-air/outdoor/air-quality-index.html|archive-date=28 August 2015|access-date=20 August 2015}}</ref>
* हवा के उत्सर्जन के कम करे खातिर स्वैच्छिक उपाय सभ के प्रोत्साहित करे खातिर "एक्शन डे" घोषित कइल, जइसे कि सार्वजनिक परिवहन के इस्तेमाल।<ref>{{Cite web|url=http://www.sparetheair.org/Make-a-Difference/Spare-the-Air-Every-Day/Summer.aspx|title=Spare the Air - Summer Spare the Air|archive-url=https://web.archive.org/web/20180611003730/http://sparetheair.org/Make-a-Difference/Spare-the-Air-Every-Day/Summer.aspx|archive-date=11 June 2018|access-date=20 August 2015}}</ref>
* फेफड़ा में महीन कण के प्रवेश से बचावे खातिर मास्क के इस्तेमाल के सलाह दिहल जाला।<ref>{{Cite web|url=https://www.moh.gov.sg/content/moh_web/home/pressRoom/Current_Issues/2014/haze/faq--use-of-masks-and-availability-of-masks.html|title=FAQ: Use of masks and availability of masks|archive-url=https://web.archive.org/web/20180622004913/https://www.moh.gov.sg/content/moh_web/home/pressRoom/Current_Issues/2014/haze/faq--use-of-masks-and-availability-of-masks.html|archive-date=2018-06-22|access-date=20 August 2015}}</ref>
बहुत खराब हवा के क्वालिटी के दौर के दौरान, जइसे कि वायु प्रदूषण के एपिसोड, जब एक्यूआई बतावे ला कि खतरनाक जोखिम से जनस्वास्थ्य के काफी नुकसान हो सके ला, एजेंसी सभ आपातकालीन योजना सभ के इस्तेमाल क सके लीं जेह में ऊ लोग प्रमुख उत्सर्जक (जइसे कि कोयला जरे वाला उद्योग) के ऑर्डर दे सके ला ) के उत्सर्जन में कटौती करे खातिर जब तक कि खतरनाक स्थिति कम ना हो जाव।
अधिकतर हवा के दूषित पदार्थ सभ में एकरा से जुड़ल एक्यूआई ना होला। कई देस सभ जमीनी स्तर के ओजोन, [[पार्टिकुलेट मैटर|कण]], [[सल्फर डाइऑक्साइड]], कार्बन मोनोऑक्साइड आ नाइट्रोजन डाइऑक्साइड के निगरानी करे लें आ एह प्रदूषक सभ खातिर हवा क्वालिटी इंडेक्स के गणना करे लें।<ref name="aqi_basic">{{Cite web|url=http://www.airnow.gov/index.cfm?action=aqibasics.aqi|title=Air Quality Index (AQI) - A Guide to Air Quality and Your Health|date=9 December 2011|publisher=US EPA|archive-url=https://web.archive.org/web/20180618144741/https://airnow.gov/index.cfm?action=aqibasics.aqi|archive-date=18 June 2018|access-date=8 August 2012}}</ref>
कवनो खास राष्ट्र में एक्यूआई के परिभाषा ओह राष्ट्र में राष्ट्रीय हवा के गुणवत्ता मानक के विकास के आसपास के प्रवचन के दर्शावेले।<ref>{{Cite web|url=https://www.wsj.com/articles/the-epas-latest-threat-to-economic-growth-1407970689|title=The EPA's Latest Threat to Economic Growth|last=Jay Timmons|date=13 August 2014|website=WSJ|access-date=20 August 2015}}</ref> दुनिया में कहीं भी सरकारी एजेंसी हवा के गुणवत्ता सूचकांक के एगो आम परिभाषा के इस्तेमाल से प्रदर्शन खातिर आपन रियल टाइम एयर मॉनिटरिंग डेटा जमा करे के इजाजत देवे वाली वेबसाइट हाल में उपलब्ध हो गइल बा।<ref>{{Cite web|url=http://waqi.info/|title=World Air Quality Index|access-date=20 August 2015}}</ref>
== लोकेशन के हिसाब से इंडेक्स ==
=== भारत ===
राष्ट्रीय वायु गुणवत्ता सूचकांक (AQI) के शुरुआत 17 सितंबर 2014 के [[नई दिल्ली]] में [[स्वच्छ भारत अभियान]] के तहत भइल रहे।<ref>{{Cite news|url=https://www.washingtonpost.com/blogs/worldviews/wp/2014/10/17/india-launches-its-own-air-quality-index-can-its-numbers-be-trusted/|title=India launches its own Air Quality Index. Can its numbers be trusted?|last=Rama Lakshmi|date=October 17, 2014|work=Washington Post|access-date=August 20, 2015}}</ref><ref>{{Cite news|url=https://www.business-standard.com/article/government-press-release/national-air-quality-index-aqi-launched-by-the-environment-minister-aqi-114101700961_1.html|title=National Air Quality Index (AQI) launched by the Environment Minister AQI is a huge initiative under 'Swachh Bharat' AQI to act as 'One Number- One Colour-One Description' to judge the Air Qualit|date=17 October 2014|work=Business Standard India}}</ref><ref>{{Cite news|url=https://www.firstpost.com/india/from-safar-to-national-air-quality-index-how-and-when-india-started-monitoring-aqi-10143261.html|title=Air Quality Index in India: When it was set up and how it is measured|date=17 November 2021|work=Firstpost|language=en}}</ref><ref>{{Cite news|url=https://pib.gov.in/newsite/printrelease.aspx?relid=110654#:~:text=National%20Air%20Quality%20Index%20(AQI,Environment%2C%20Forest%20and%20Climate%20Change.&text=The%20Minister%20for%20Environment%2C%20Forests,(AQI)%20in%20New%20Delhi.|title=National Air Quality Index (AQI) launched by the Environment Minister AQI is a huge initiative under 'Swachh Bharat'|work=pib.gov.in}}</ref>
केंद्रीय प्रदूषण नियंत्रण बोर्ड राज्य प्रदूषण नियंत्रण बोर्डन के साथे मिल के देश के 240 शहरन के शामिल करत राष्ट्रीय वायु निगरानी कार्यक्रम (एनएएमपी) चलावत आइल बा जवना में 342 से अधिका निगरानी स्टेशन बा.<ref>{{Cite web|url=http://www.ppsthane.com/blog/ambient-air-quality-monitoring-stations|title=Ambient Air Quality Monitoring Stations|date=2016-08-15|language=en-US|access-date=2016-08-16}}</ref> मेडिकल प्रोफेशनल, एयर क्वालिटी एक्सपर्ट, अकादमी, एडवोकेसी ग्रुप, आ एसपीसीबी के एगो एक्सपर्ट ग्रुप के गठन कइल गइल आ आईआईटी कानपुर के एगो तकनीकी अध्ययन दिहल गइल. आईआईटी कानपुर आ एक्सपर्ट ग्रुप 2014 में एक्यूआई योजना के सिफारिश कइलस।<ref>{{Cite web|url=http://home.iitk.ac.in/~mukesh/indian%20air%20quality.html|title=Indian Air Quality Index|archive-url=https://web.archive.org/web/20141107224911/http://home.iitk.ac.in/~mukesh/indian%20air%20quality.html|archive-date=2014-11-07|access-date=2014-10-21}}</ref> जबकि पहिले के माप सूचकांक तीन संकेतक तक सीमित रहे, लेकिन नयका सूचकांक में आठ पैरामीटर के माप कइल गइल बा।<ref>{{Cite web|url=http://pib.nic.in/newsite/PrintRelease.aspx?relid=110654|title=National Air Quality Index (AQI) launched by the Environment Minister AQI is a huge initiative under 'Swachh Bharat'|access-date=20 August 2015}}</ref> लगभग रियल टाइम आधार पर डेटा देवे वाला लगातार निगरानी प्रणाली नई दिल्ली, [[मुंबई]], [[पुणे]], [[कलकत्ता|कोलकाता]] अउरी [[अहमदाबाद]] में लगावल गइल बा .<ref>{{Cite news|url=http://economictimes.indiatimes.com/news/politics-and-nation/india-launches-index-to-measure-air-quality/etvertical_articleshow/44855452.cms|title=India launches index to measure air quality|work=timesofindia-economictimes|access-date=20 August 2015}}</ref>
एक्यूआई के छह गो श्रेणी बा, जवना में अच्छा, संतोषजनक, मध्यम, खराब, गंभीर, आ खतरनाक बा। प्रस्तावित एक्यूआई में आठ गो प्रदूषक (PM {{Sub|10}}, PM {{Sub|2.5}}, NO {{Sub|2}}, SO {{Sub|2}}, CO, O {{Sub|3}}, NH {{Sub|3}}, आ Pb) पर बिचार कइल जाई जिनहन खातिर अल्पकालिक (24 घंटा के औसत अवधि तक) राष्ट्रीय परिवेश हवा के गुणवत्ता मानक निर्धारित कइल जाला।<ref>{{Cite web|url=http://www.cpcb.nic.in|title=::: Central Pollution Control Board :::|access-date=20 August 2015}}</ref> नापल गइल परिवेश के एकाग्रता, संबंधित मानक आ संभावित स्वास्थ्य परभाव के आधार पर, एह में से हर प्रदूषक खातिर एगो उप-सूचकांक के गणना कइल जाला। सबसे खराब उप-सूचकांक समग्र एक्यूआई के दर्शावत बा। अलग-अलग एक्यूआई श्रेणी आ प्रदूषक सभ खातिर संभावित स्वास्थ्य परभाव के भी सुझाव दिहल गइल बा, समूह के मेडिकल बिसेसज्ञ लोग के प्राथमिक इनपुट के साथ। एक्यूआई मान आ संबंधित परिवेश के एकाग्रता (स्वास्थ्य ब्रेकपॉइंट) के साथे-साथ चिन्हित आठ गो प्रदूषक सभ खातिर संबंधित संभावित स्वास्थ्य परभाव निम्नलिखित बाड़ें:
{| class="wikitable"
|+एक्यूआई श्रेणी, प्रदूषक आ स्वास्थ्य ब्रेकपॉइंट
| style="background: #9cf; colour: black; font-weight:bold" | एक्यूआई श्रेणी (रेंज) ।
| style="background: #9cf; colour: black; font-weight:bold" | पीएम {{Sub|10}} (24 घंटा) ।
| style="background: #9cf; colour: black; font-weight:bold" | पीएम {{Sub|2.5}} (24 घंटा) ।
| style="background: #9cf; colour: black; font-weight:bold" | नंबर {{Sub|2}} (24घंटा)
| style="background: #9cf; colour: black; font-weight:bold" | ओ {{Sub|3}} (8घंटा)
| style="background: #9cf; colour: black; font-weight:bold" | सीओ (8घंटा)
| style="background: #9cf; colour: black; font-weight:bold" | एसओ {{Sub|2}} (24घंटा)
| style="background: #9cf; colour: black; font-weight:bold" | एनएच {{Sub|3}} (24घंटा)
| style="background: #9cf; colour: black; font-weight:bold" | पीबी (24 घंटा)
| style="background: #9cf; colour: black; font-weight:bold" | रंग
|-
| style="background:#00b050; colour: black" | बढ़िया (0-50)
| style="background:#00b050; colour: black" | 0-50
| style="background:#00b050; colour: black" | 0-30
| style="background:#00b050; colour: black" | 0-40
| style="background:#00b050; colour: black" | 0-50
| style="background:#00b050; colour: black" | 0-1.0
| style="background:#00b050; colour: black" | 0-40
| style="background:#00b050; colour: black" | 0-200
| style="background:#00b050; colour: black" | 0-0.5
| style="background:#00b050; colour: black" | गहिराह हरियर रंग
|-
| style="background:#92d050; colour: black" | संतोषजनक (51-100)
| style="background:#92d050; colour: black" | 51-100
| style="background:#92d050; colour: black" | 31-60
| style="background:#92d050; colour: black" | 41-80
| style="background:#92d050; colour: black" | 51-100
| style="background:#92d050; colour: black" | 1.1-2.0
| style="background:#92d050; colour: black" | 41-80
| style="background:#92d050; colour: black" | 201-400
| style="background:#92d050; colour: black" | 0.5-1.0
| style="background:#92d050; colour: black" | हल्का हरियर रंग के होला
|-
| style="background: #ff0; colour: black" | मध्यम (101-200)
| style="background: #ff0; colour: black" | 101-250
| style="background: #ff0; colour: black" | 61-90
| style="background: #ff0; colour: black" | 81-180
| style="background: #ff0; colour: black" | 101-168
| style="background: #ff0; colour: black" | 2.1-10
| style="background: #ff0; colour: black" | 81-380
| style="background: #ff0; colour: black" | 401-800
| style="background: #ff0; colour: black" | 1.1-2.0
| style="background: #ff0; colour: black" | पियर
|-
| style="background: #ff6500; colour: black" | गरीब (201-300)
| style="background: #ff6500; colour: black" | 251-350
| style="background: #ff6500; colour: black" | 91-120
| style="background: #ff6500; colour: black" | 181-280
| style="background: #ff6500; colour: black" | 169-208
| style="background: #ff6500; colour: black" | 10-17
| style="background: #ff6500; colour: black" | 381-800
| style="background: #ff6500; colour: black" | 801-1200
| style="background: #ff6500; colour: black" | 2.1-3.0
| style="background: #ff6500; colour: black" | संतरा
|-
| style="background: #ff0000; color: white" | गंभीर (301-400)
| style="background: #ff0000; color: white" | 351-430
| style="background: #ff0000; color: white" | 121-250
| style="background: #ff0000; color: white" | 281-400
| style="background: #ff0000; color: white" | 209-748
| style="background: #ff0000; color: white" | 17-34
| style="background: #ff0000; color: white" | 801-1600
| style="background: #ff0000; color: white" | 1200-1800
| style="background: #ff0000; color: white" | 3.1-3.5
| style="background: #ff0000; color: white" | लाल
|-
| style="background: #c00000; color: white" | खतरनाक (401-500)
| style="background: #c00000; color: white" | 430+
| style="background: #c00000; color: white" | 250+
| style="background: #c00000; color: white" | 400+
| style="background: #c00000; color: white" | 748+
| style="background: #c00000; color: white" | 34+
| style="background: #c00000; color: white" | 1600+
| style="background: #c00000; color: white" | 1800+
| style="background: #c00000; color: white" | 3.5+
| style="background: #c00000; color: white" | मैरून
|}
{| class="wikitable"
!एक्यूआई
! सेहत पर परभाव
|-
| बढ़िया (0-50)
| कम से कम असर पड़े
|-
| संतोषजनक (51-100)
| संवेदनशील लोग के सांस लेवे में मामूली परेशानी हो सक ता
|-
| मध्यम (101-200)
| फेफड़ा के बेमारी जइसे कि दमा से पीड़ित लोग के साँस लेवे में परेशानी हो सकेला, आ दिल के बेमारी से पीड़ित लोग
|-
| गरीब (201-300)
| लंबा समय तक संपर्क में रहला प लोग के सांस लेवे में परेशानी हो सक ता
|-
| बहुत गरीब (301-400)
| लंबा समय तक संपर्क में रहला प लोग के सांस के बेमारी हो सक ता
|-
| गम्हीर
(401-500)
| स्वस्थ लोग पर भी साँस के प्रभाव पड़ सकेला, आ फेफड़ा/हृदय के बेमारी से पीड़ित लोग पर स्वास्थ्य पर गंभीर प्रभाव पड़ सकेला
|}
=== अमेरिका ===
[[चित्र:Pm25-24a-super.gif|thumb|पीएम <sub>2.5</sub> 24 घंटा के एक्यूआई लूप, यूएस ईपीए के सौजन्य से]]
अमेरिका के पर्यावरण संरक्षण एजेंसी (ईपीए) एगो हवा के गुणवत्ता सूचकांक बनवले बिया जवना के इस्तेमाल हवा के गुणवत्ता के रिपोर्ट करे खातिर कइल जाला. एह एक्यूआई के छह श्रेणी में बाँटल गइल बा जवन स्वास्थ्य चिंता के बढ़त स्तर के संकेत देत बा। 300 से ढेर एक्यूआई मान खतरनाक हवा के गुणवत्ता के प्रतिनिधित्व करे ला आ 50 से नीचे हवा के गुणवत्ता बढ़िया होला।<ref name="aqi_basic" />
{|
|
{| style="width:480px; float:left;" cellspacing="0" border="2"
| style="width:31%;" | '''<span style="color:#fff; font-family:arial; font-size:1em;">हवा के गुणवत्ता सूचकांक (एक्यूआई) के मान</span>'''
| style="background:#036; width:32%;" | <span style="color:#fff; font-family:arial; font-size:1em;">'''स्वास्थ्य के चिंता के स्तर'''</span>
| style="background:#036; width:37%;" | <span style="color:#fff; font-family:arial; font-size:1em;">'''रंग-बिरंग '''</span>
|- style="vertical-align:top; background:#00e400;"
| style="width:31%;" | 0 से 50 तक
| style="width:32%;" | नीमन
| style="width:37%;" | हरियर
|- style="vertical-align:top; background:#ff0;"
| style="width:31%;" | 51 से 100 तक
| style="width:32%;" | उदार
| style="width:37%;" | पियर
|- style="vertical-align:top; background:#ff7e00;"
| style="width:31%;" | 101 से 150 तक
| style="width:32%;" | संवेदनशील समूह खातिर अस्वस्थ बा
| style="width:37%;" | संतरा
|- style="vertical-align:top; background:#f00;"
| style="width:31%;" | <span style="color:#fff;">151 से 200 तक </span>
| style="width:32%;" | <span style="color:#fff;">अस्वस्थ बा</span>
| style="width:37%;" | <span style="color:#fff;">लाल</span>
|- style="vertical-align:top; background:#8f3f97;"
| style="width:31%;" | <span style="color:#fff;">201 से 300 तक </span>
| style="width:32%;" | <span style="color:#fff;">बहुत अस्वस्थ बा</span>
| style="width:37%;" | <span style="color:#fff;">बैंगनी</span>
|- style="vertical-align:top; background:#7e0023;"
| style="width:31%;" | <span style="color:#fff;">301 से 500 तक </span>
| style="width:32%;" | <span style="color:#fff;">खतरनाक बा</span>
| style="width:37%;" | <span style="color:#fff;">मैरून </span>
|}
|}
एक्यूआई स्वच्छ हवा अधिनियम के तहत नियंत्रित पाँच गो "मापदण्ड" प्रदूषक सभ पर आधारित बा : जमीनी स्तर के ओजोन, कण पदार्थ, कार्बन मोनोऑक्साइड, सल्फर डाइऑक्साइड, आ नाइट्रोजन डाइऑक्साइड। ईपीए एह में से हर प्रदूषक खातिर नेशनल एम्बिएंट एयर क्वालिटी स्टैंडर्ड (NAAQS) के स्थापना कइले बा जेह से कि जनस्वास्थ्य के रक्षा कइल जा सके। 100 के AQI मान आमतौर पर प्रदूषक खातिर NAAQS के स्तर से मेल खाला।<ref name="aqi_basic" /> स्वच्छ हवा अधिनियम (अमेरिका) (1990) में ईपीए के हर पांच साल पर अपना राष्ट्रीय परिवेश हवा के गुणवत्ता मानक के समीक्षा करे के कहल गइल बा ताकि स्वास्थ्य परभाव के बिकसित जानकारी के देखावल जा सके। एह बदलावन के देखावे खातिर हवा के गुणवत्ता सूचकांक में समय-समय पर समायोजन कइल जाला।
{{clear}}
== इहो देखल जाय ==
* [[हवा प्रदूषण]]
* [[पार्टिकुलेट मैटर]]
== संदर्भ ==
{{Reflist|33em}}
== बाहरी कड़ी ==
* [https://www.aqi.in/ भारत में एक्यूआई], रियल टाइम आँकड़ा देखे खातिर।
{{प्रदूषण}}
[[श्रेणी:प्रदूषण]]
[[श्रेणी:हवा प्रदूषण]]
[[श्रेणी:पृथिवी के वायुमंडल]]
n6ntotpf4yqniphi44pk7n4yr2yz0yq
Module:Infobox/styles.css
828
88925
803575
736022
2024-04-08T01:53:22Z
en>Jdlrobson
0
Apply night theme to infobox full data so links are accessible
803575
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: */
html.skin-theme-clientpref-night .infobox-full-data * {
background: #37373e !important;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data * {
background: #37373e !important;
}
}
8wvaa7xentd53bc6gbh49loqk1wudb3
803576
803575
2024-04-08T01:55:17Z
en>Jdlrobson
0
Add some example pages and slightly darken background
803576
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data * {
background: #1f1f23 !important;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data * {
background: #1f1f23 !important;
}
}
pquqexq7eyt2nfewo6qikaml34jw9ru
803577
803576
2024-04-08T01:57:25Z
en>Jdlrobson
0
.... and of course color-base is needed.
803577
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data * {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data * {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
tvhiop4s8igknittm8zr1gazaetnqp6
803578
803577
2024-04-09T15:09:30Z
en>Jdlrobson
0
Use div so this doesn't apply to
803578
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data div {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data div {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
n794oomzam5ytazh5fgew7phq5hivoe
803579
803578
2024-06-12T18:43:28Z
en>Jdlrobson
0
Restrict to elements not tagged notheme to allow templates to override
803579
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
8o1tllmn0leabq7gu8vk3fyiu6kz5hl
803580
803579
2024-06-13T21:46:42Z
en>Jon (WMF)
0
Applies fix for [[File:Broken_infobox_of_Chris_Dangerfield_2024-06-13.png]]
803580
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Overrides restriction of max width on infoboxes until templates such as [[Template:Infobox_football_biography]] have been adopted for mobile screens ([[File:Broken_infobox_of_Chris_Dangerfield_2024-06-13.png]]) */
body.skin--responsive.skin-vector-2022 .mw-parser-output .infobox {
max-width: none !important;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
lj0tohecldp0bwj0b2u1qej5bqtrdyi
803581
803580
2024-06-13T21:49:48Z
en>Jon (WMF)
0
Drop parser output and add media query. Follow up to previous edit.
803581
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Overrides restriction of max width on infoboxes until templates such as [[Template:Infobox_football_biography]] have been adopted for mobile screens ([[File:Broken_infobox_of_Chris_Dangerfield_2024-06-13.png]]) */
@media (min-width: 640px) {
body.skin--responsive.skin-vector-2022 .infobox {
max-width: none !important;
}
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
b4p3hfepnkcv3u5ikna79azc0nx9dfm
803582
803581
2024-06-13T22:16:17Z
en>Jon (WMF)
0
Rollback. This has unexpected consequences on other pages e.g. [[Spain]]
803582
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
8o1tllmn0leabq7gu8vk3fyiu6kz5hl
803583
803582
2024-06-14T01:15:22Z
en>Jon (WMF)
0
Add infobox-table class to markup infoboxes that rely on the table tag that is associated with infoboxes.
803583
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to add columns and rows to the infobox itself rather than as part of a new table inside them. This class should be discouraged and removed on the long term, but allows us to at least identify these tables going forward
Currently in use on: [[Module:Infobox3cols]]
Fixes issue described in https://phabricator.wikimedia.org/F55300125 on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > tbody,
body.skin--responsive .infobox-table > caption,
body.skin--responsive .infobox-table tr {
display: table-row !important;
}
}
emm86c0lv2x2y4ikxjx06ztp6ejc9mw
803584
803583
2024-06-14T03:50:54Z
en>Jdlrobson
0
Reduce padding, to reduce overall size of infobox-table elements
803584
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to add columns and rows to the infobox itself rather than as part of a new table inside them. This class should be discouraged and removed on the long term, but allows us to at least identify these tables going forward
Currently in use on: [[Module:Infobox3cols]]
Fixes issue described in https://phabricator.wikimedia.org/F55300125 on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > tbody,
body.skin--responsive .infobox-table > caption,
body.skin--responsive .infobox-table tr {
display: table-row !important;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
rua0wcb71j23d3o3okm2ve04d8d3k1l
803585
803584
2024-06-14T03:58:50Z
en>Jdlrobson
0
Limit the !important's to inline style override
803585
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to add columns and rows to the infobox itself rather than as part of a new table inside them. This class should be discouraged and removed on the long term, but allows us to at least identify these tables going forward
Currently in use on: [[Module:Infobox3cols]]
Fixes issue described in https://phabricator.wikimedia.org/F55300125 on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > tbody,
body.skin--responsive .infobox-table > caption,
body.skin--responsive .infobox-table tr {
display: table-row !important;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
biq4f9es7pzhqssg4qc5dos3kfefhkl
803586
803585
2024-06-14T22:36:41Z
en>Jdlrobson
0
Use display table-caption for captions
803586
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to add columns and rows to the infobox itself rather than as part of a new table inside them. This class should be discouraged and removed on the long term, but allows us to at least identify these tables going forward
Currently in use on: [[Module:Infobox3cols]]
Fixes issue described in https://phabricator.wikimedia.org/F55300125 on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody,
body.skin--responsive .infobox-table tr {
display: table-row !important;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
1mum59t79jnsox9rh835onlc51dys1n
803587
803586
2024-06-14T23:05:28Z
en>Jdlrobson
0
For completeness.. use table-row-group for tbody element
803587
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
@media (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to add columns and rows to the infobox itself rather than as part of a new table inside them. This class should be discouraged and removed on the long term, but allows us to at least identify these tables going forward
Currently in use on: [[Module:Infobox3cols]]
Fixes issue described in https://phabricator.wikimedia.org/F55300125 on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table tr {
display: table-row !important;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
5scbs9zau8glf2g6gia4w61ymkkgomb
803588
803587
2024-07-31T23:21:15Z
en>Jon (WMF)
0
Fixes [[phab:T369874]] via automated script
803588
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
body.skin-minerva .infobox-header,
body.skin-minerva .infobox-subheader,
body.skin-minerva .infobox-above,
body.skin-minerva .infobox-title,
body.skin-minerva .infobox-image,
body.skin-minerva .infobox-full-data,
body.skin-minerva .infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to add columns and rows to the infobox itself rather than as part of a new table inside them. This class should be discouraged and removed on the long term, but allows us to at least identify these tables going forward
Currently in use on: [[Module:Infobox3cols]]
Fixes issue described in https://phabricator.wikimedia.org/F55300125 on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table tr {
display: table-row !important;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
7pwddboq2hsp05urboa0k08jc5go6da
803589
803588
2024-11-12T17:39:02Z
en>Izno
0
no reason for that
803589
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* T281642 */
.infobox-header,
.infobox-subheader,
.infobox-above,
.infobox-title,
.infobox-image,
.infobox-full-data,
.infobox-below {
text-align: center;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to add columns and rows to the infobox itself rather than as part of a new table inside them. This class should be discouraged and removed on the long term, but allows us to at least identify these tables going forward
Currently in use on: [[Module:Infobox3cols]]
Fixes issue described in https://phabricator.wikimedia.org/F55300125 on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table tr {
display: table-row !important;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
9u09w99811h4o10ugyqx5jzhofifgjq
803590
803589
2024-11-12T17:45:34Z
en>Izno
0
remove this block entirely, common.css and minerva.css have the relevant styles
803590
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to add columns and rows to the infobox itself rather than as part of a new table inside them. This class should be discouraged and removed on the long term, but allows us to at least identify these tables going forward
Currently in use on: [[Module:Infobox3cols]]
Fixes issue described in https://phabricator.wikimedia.org/F55300125 on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table tr {
display: table-row !important;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
2pxfxg7rwc3jv0w8wyi0wqelqle32m2
803591
803590
2025-05-08T15:43:01Z
en>Izno
0
remove .infobox-table tr rule per [[Template talk:Infobox#Display for table rows]]
803591
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) div:not(.notheme) {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
0drczzpwouuzdu6lmdtibxmcwzo6l44
803592
803591
2025-06-16T15:26:13Z
en>Izno
0
per talk page
803592
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
ed9hwgdet8h6h2bc3c9iiv47kgu74p6
803593
803592
2025-10-06T00:12:24Z
en>Jonesey95
0
fix text color in dark mode; this generally works but may cause problems, in which case revert or try specifying a different CSS value. background-color: transparent is generally not advised, but if it is necessary, it needs color:inherit.
803593
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
color:inherit;
}
.infobox-3cols-child {
margin: auto;
}
.infobox .navbar {
font-size: 100%;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
5h4nxyw7y2buszwy42v81sw59h2ytd1
803594
803593
2025-10-10T07:08:27Z
en>Zackmann08
0
attempting to resolve a nesting issue.
803594
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
color:inherit;
}
.infobox-3cols-child {
margin: -3px;
}
.infobox .navbar {
font-size: 100%;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
et19aj2zpnqpz3yxaypsrg4fv3vb8t6
803595
803594
2026-05-09T23:05:29Z
en>Zackmann08
0
testing directly implementing [[Template:Longitem]] for infobox labels.
803595
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
color:inherit;
}
.infobox-3cols-child {
margin: -3px;
}
.infobox .navbar {
font-size: 100%;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Directly implement [[Template:Longitem]] for infobox labels. */
th.infobox-label {
display: inline-block;
line-height: 1.2em;
padding: .1em;
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
o2lmhj317m0wkh4z3i4jye0edyjtgo0
803596
803595
2026-05-10T02:18:05Z
en>Zackmann08
0
Undid revision [[Special:Diff/1353378980|1353378980]] by [[Special:Contributions/Zackmann08|Zackmann08]] ([[User talk:Zackmann08|talk]]) possibly broke something...
803596
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
color:inherit;
}
.infobox-3cols-child {
margin: -3px;
}
.infobox .navbar {
font-size: 100%;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
et19aj2zpnqpz3yxaypsrg4fv3vb8t6
803597
803596
2026-06-05T20:49:48Z
en>Krinkle
0
Add basic layout (float/clear/width/margin-left) so that in [[mw:Manual:Safemode|safemode]] most articles are at least decently readable. I am deliberately not yet deleting from [[MediaWiki:Common.css]] because still is stil used in countless places without this module, and therefore to avoid bloat, I'm not adding the rest of the colors and other more detailed styles.
803597
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/* NOTE: This is maintained both here and in [[MediaWiki:Common.css]] until migration is complete.
* Starting with bare minimum for the benefit of [[mw:mManual:Safemode]]. */
@media (min-width: 640px) {
.infobox {
/* @noflip */
margin-left: 1em;
/* @noflip */
float: right;
/* @noflip */
clear: right;
width: 22em;
}
}
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
color:inherit;
}
.infobox-3cols-child {
margin: -3px;
}
.infobox .navbar {
font-size: 100%;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
j2wwjoocxn9i57ypq18ttmt9bzysi82
803598
803597
2026-06-05T20:49:55Z
en>Krinkle
0
803598
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/* NOTE: This is maintained both here and in [[MediaWiki:Common.css]] until migration is complete.
* Starting with bare minimum for the benefit of [[mw:Manual:Safemode]]. */
@media (min-width: 640px) {
.infobox {
/* @noflip */
margin-left: 1em;
/* @noflip */
float: right;
/* @noflip */
clear: right;
width: 22em;
}
}
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
color:inherit;
}
.infobox-3cols-child {
margin: -3px;
}
.infobox .navbar {
font-size: 100%;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
1cfxsygzesgd9qp9tomkwlfeh7sk09g
803599
803598
2026-06-08T00:49:01Z
en>Zackmann08
0
Implementing new class for hiding extra rows.
803599
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/* NOTE: This is maintained both here and in [[MediaWiki:Common.css]] until migration is complete.
* Starting with bare minimum for the benefit of [[mw:Manual:Safemode]]. */
@media (min-width: 640px) {
.infobox {
/* @noflip */
margin-left: 1em;
/* @noflip */
float: right;
/* @noflip */
clear: right;
width: 22em;
}
}
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
color:inherit;
}
.infobox-3cols-child {
margin: -3px;
}
.infobox .navbar {
font-size: 100%;
}
.infobox-hiddenrow {
display: none;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
qlld126pj5n6tifvwjuvk0jxst79dv3
803600
803599
2026-06-09T21:49:07Z
en>Izno
0
sigh
803600
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/* NOTE: This is maintained both here and in [[MediaWiki:Common.css]] until migration is complete.
* Starting with bare minimum for the benefit of [[mw:Manual:Safemode]]. */
@media (min-width: 640px) {
.infobox {
/* @noflip */
margin-left: 1em;
/* @noflip */
float: right;
/* @noflip */
clear: right;
width: 22em;
}
}
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
color:inherit;
}
.infobox-3cols-child {
margin: -3px;
}
.infobox .navbar {
font-size: 100%;
}
/* remove when infobox is not a table anymore */
.infobox-hiddenrow,
/* we mean it, Minerva. but also Vector 2022 in the future at some point */
body.skin--responsive.skin--responsive .infobox .infobox-hiddenrow {
display: none;
}
/* Dark theme: [[William_Wragg]], [[Coral_Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
rl8j583dyum0u1mdd4ip8cx64nsci33
803601
803600
2026-06-09T21:49:34Z
en>Izno
0
spaceeeee
803601
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/* NOTE: This is maintained both here and in [[MediaWiki:Common.css]] until migration is complete.
* Starting with bare minimum for the benefit of [[mw:Manual:Safemode]]. */
@media (min-width: 640px) {
.infobox {
/* @noflip */
margin-left: 1em;
/* @noflip */
float: right;
/* @noflip */
clear: right;
width: 22em;
}
}
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
color:inherit;
}
.infobox-3cols-child {
margin: -3px;
}
.infobox .navbar {
font-size: 100%;
}
/* remove when infobox is not a table anymore */
.infobox-hiddenrow,
/* we mean it, Minerva. but also Vector 2022 in the future at some point */
body.skin--responsive.skin--responsive .infobox .infobox-hiddenrow {
display: none;
}
/* Dark theme: [[William Wragg]], [[Coral Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
bi1nsztkx4350a55cuzjhaotvp5h216
803602
803601
2026-07-30T00:18:39Z
SM7
3953
27 revisions imported from [[:en:Module:Infobox/styles.css]]
803601
sanitized-css
text/css
/* {{pp|small=y}} */
/*
* This TemplateStyles sheet deliberately does NOT include the full set of
* infobox styles. We are still working to migrate all of the manual
* infoboxes. See [[MediaWiki talk:Common.css/to do#Infobox]]
* DO NOT ADD THEM HERE
*/
/* NOTE: This is maintained both here and in [[MediaWiki:Common.css]] until migration is complete.
* Starting with bare minimum for the benefit of [[mw:Manual:Safemode]]. */
@media (min-width: 640px) {
.infobox {
/* @noflip */
margin-left: 1em;
/* @noflip */
float: right;
/* @noflip */
clear: right;
width: 22em;
}
}
/*
* not strictly certain these styles are necessary since the modules now
* exclusively output infobox-subbox or infobox, not both
* just replicating the module faithfully
*/
.infobox-subbox {
padding: 0;
border: none;
margin: -3px;
width: auto;
min-width: 100%;
font-size: 100%;
clear: none;
float: none;
background-color: transparent;
color:inherit;
}
.infobox-3cols-child {
margin: -3px;
}
.infobox .navbar {
font-size: 100%;
}
/* remove when infobox is not a table anymore */
.infobox-hiddenrow,
/* we mean it, Minerva. but also Vector 2022 in the future at some point */
body.skin--responsive.skin--responsive .infobox .infobox-hiddenrow {
display: none;
}
/* Dark theme: [[William Wragg]], [[Coral Castle]] */
@media screen {
html.skin-theme-clientpref-night .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .infobox-full-data:not(.notheme) > div:not(.notheme)[style] {
background: #1f1f23 !important;
/* switch with var( --color-base ) when supported. */
color: #f8f9fa;
}
}
/* Since infobox is a table, many infobox templates take advantage of this to
* add columns and rows to the infobox itself rather than as part of a new table
* inside them. This class should be discouraged and removed on the long term,
* but allows us to at least identify these tables going forward
* Currently in use on: [[Module:Infobox3cols]]
* Fixes issue described in [[phab:F55300125]] on Vector 2022.
*/
@media (min-width: 640px) {
body.skin--responsive .infobox-table {
display: table !important;
}
body.skin--responsive .infobox-table > caption {
display: table-caption !important;
}
body.skin--responsive .infobox-table > tbody {
display: table-row-group;
}
body.skin--responsive .infobox-table th,
body.skin--responsive .infobox-table td {
padding-left: inherit;
padding-right: inherit;
}
}
bi1nsztkx4350a55cuzjhaotvp5h216
शीला दीक्षित
0
88968
803301
763125
2026-07-29T16:06:56Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803301
wikitext
text/x-wiki
{{Infobox officeholder
| name = शीला दीक्षित
| image = Sheila Dikshit Ji.jpg
| caption =
| office1 = प्रदेश कांग्रेस कमेटी, दिल्ली
| term_start1 = 11 जनवरी 2019
| term_end1 = 20 जुलाई 2019
| 1blankname1 = राष्ट्रीय अध्यक्ष (कांग्रेस)
| 1namedata1 = [[राहुल गांधी]]
| predecessor1 =अजय मांकड़
| office2 = केरल के 20वीं राज्यपाल
| 1blankname2 = मुख्यमंत्री
| 1namedata2 = ओमान चांडी
| predecessor2 = निखिल कुमार
| successor2 = पी. सतशिवम<ref>{{cite web | title=Sathasivam becomes Kerala governor, to take oath on September 5 | website=India Today | date=3 September 2014 | url=http://indiatoday.intoday.in/story/sathasivam-kerala-governor-cji-home-ministry-supreme-court/1/380730.html | access-date=29 January 2016}}</ref>
| term_start2 = 11 मार्च 2014
| term_end2 = 4 सितंबर 2014
<!-- -->| office3 =दिल्ली के 6वीं मुख्यमंत्री
| predecessor3 = [[सुषमा स्वराज]]
| successor3 = [[अरविंद केजरीवाल]]
| lieutenant_governor3 =
| term_start3 = 4 दिसंबर 1998
| term_end3 = 27 दिसंबर 2013
<!-- -->| office4 = दिल्ली बिधानसभा बिधायक
| predecessor5 = कीर्ति आजाद
| successor5 = ''समाप्त''
| constituency5 = गोल मार्केट
| term_start5 = 3 दिसंबर 1998
| term_end5 = 3 दिसंबर 2008
<!-- -->| office5 =
| predecessor4 = ''स्थाप.''
| successor4 = [[अरविंद केजरीवाल]]
| constituency4 = नई दिल्ली
| term_start4 = 4 दिसंबर 2008
| term_end4 = 28 दिसंबर 2013
<!-- -->| office6 = [[सांसद, लोकसभा]]
| constituency6 = कन्नौज
| predecessor6 = छोटे सिंह यादव
| successor6 = छोटे सिंह यादव
| term_start6 = 1984
| term_end6 = 1989
<!-- -->| office7 =भारतीय डेलिगेशन के सदस्य <br /> यूनाइटेड नेशंस कमीशन ऑन वूमन अफेयर्स
| primeminister7 = [[इंदिरा गांधी]] <br /> [[राजीव गांधी]]
| term_start7 = 1984
| term_end7 = 89
<!-- -->| office8 = संसदीय मामिला के राज्यमंत्री
| primeminister8 = [[राजीव गांधी]]
| term_start8 = 1984
| term_end8 = 1989
<!-- -->| party = [[भारतीय राष्ट्रीय कांग्रेस]]
| birth_date = {{Birth date|df=y|1938|3|31}}
| birth_place = [[कपूरथला]], [[कपूरथला राज]], [[ब्रिटिश भारत]] <br/> (आज्काल्ह: [[पंजाब]], [[भारत]])
| spouse = विनोद दीक्षित
| children = 2
| alma_mater = [[मिरांडा हाउस]], [[दिल्ली युनिवर्सिटी]]
| website =
| death_date = {{Death date and age|df=yes|2019|7|20|1938|3|31}}<ref>{{cite web |author=News Galiyara |url=https://newsgaliyara.com/three-time-delhis-chief-minister-sheila-dikshit-passed-away-at-81/ |title=Three-Time Delhi's Chief Minister Sheila Dikshit passed away at 81 |publisher=NewsGaliyara.com |date=20 July 2019 |access-date=20 July 2019 |archive-date=20 July 2019 |archive-url=https://web.archive.org/web/20190720154045/https://newsgaliyara.com/three-time-delhis-chief-minister-sheila-dikshit-passed-away-at-81/ |url-status=dead }}</ref>
| death_place = [[नई दिल्ली]], [[भारत]]
| successor1 =सुभाष चोपड़ा
| prior_term1 =
}}
'''शीला दीक्षित''' (31 मार्च 1938 — 20 जुलाई 2019) एगो भारतीय राजनीतिज्ञ रहली जे 1998 से 2013 ले दिल्ली के मुख्यमंत्री के रूप में काम कइली। एकरे अलावा, 11 मार्च 2014 से 25 अगस्त 2014 ले [[भारत]] के [[केरल]] राज्य के गवर्नर,<ref name="nbt-4mar14">{{Cite web|url=http://hindi.economictimes.indiatimes.com/delhi/politics/Former-Delhi-chief-minister-Sheila-Dikshit-is-new-governor-of-Kerala/articleshow/31422363.cms|title=शीला दीक्षित को केरल का राज्यपाल बनाया गया|date=4 मार्च 2014|publisher=नवभारत टाईम्स|access-date=5 मार्च 2014}}{{Dead link|date=September 2023 |bot=InternetArchiveBot |fix-attempted=yes }}</ref><ref name="pib-5mar14">{{Cite web|url=http://pib.nic.in/newsite/hindirelease.aspx?relid=27331|title=राज्यपालों की नियुक्ति|date=5 मार्च 2014|publisher=पत्र सूचना कार्यालय, भारत सरकार|archive-url=https://web.archive.org/web/20150530224428/http://pib.nic.in/newsite/hindirelease.aspx?relid=27331|archive-date=30 मई 2015|access-date=5 मार्च 2014}}</ref> 1984 से 1989 ले कन्नौज से संसद के सदस्य, आ 10 जनवरी 2019 से 10 जनवरी 2019 ले दिल्ली प्रदेश कांग्रेस कमेटी के अध्यक्ष रहलीं ओकर मौत हो गइल.
दीक्षित 15 साल तक [[दिल्ली]] के [[राष्ट्रिय राजधानी क्षेत्र (भारत)|राष्ट्रीय राजधानी क्षेत्र]] राज्य के [[मुख्यमंत्री]] रहले। ऊ दिल्ली के दूसरी महिला मुख्यमंत्री रहली, आ देश के पहिली महिला मुख्यमंत्री रहली जे लगातार तीन बेर मुख्यमंत्री के पद पर रहली। 17 दिसंबर 2008 के लगातार तीसरा बेर [[दिल्ली]] विधान सभा खातिर चुनल गइलें। साल 2013 के विधानसभा चुनाव में कांग्रेस के हार के बाद उनुका अपना पद से इस्तीफा देवे के पड़ल। केरल के राज्यपाल श्री निखिल कुमार के इस्तीफा के बाद उनुका के एह पद प नियुक्त कइल गइल रहे, हालांकि उ खुद 25 अगस्त के ए पद से इस्तीफा दे देले रहले। साल 2017 के उत्तर प्रदेश विधानसभा चुनाव में उनुका के कांग्रेस पार्टी के मुख्यमंत्री पद के उम्मीदवार घोषित कइल गइल रहे, लेकिन बाद में उनुकर नाम वापस ले लेले। एकरा बाद 10 जनवरी 2019 के उनुका के दिल्ली प्रदेश कांग्रेस समिति के अध्यक्ष घोषित कइल गइल . 20 जुलाई 2019 शनिवार के दिल्ली के एस्कॉर्ट अस्पताल में उनुकर निधन हो गइल।
{{clear}}
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:1938 में जनम]]
[[श्रेणी:2019 में निधन]]
{{India-politician-stub}}
rs9b967bedztkax1f9a67l5mdyk9l1s
भारतीय युवा कांग्रेस
0
88979
803316
766278
2026-07-29T16:20:24Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803316
wikitext
text/x-wiki
{{Infobox political party
| name = भारतीय युवा कांग्रेस
| native_name =
| logo = [[File:Indian Youth Congress Logo.jpg|200px]]
| colorcode = DeepSkyBlue
| president = श्रीनिवास बी वी
| secretary =
| spokesperson = अमरीश रंजन पांडे
| foundation = १९६०
| headquarters = [[नई दिल्ली]]
| international =
| website = {{url|https://iyc.in/}}
| dissolved =
| membership = २ करोड़<ref>https://m.hindustantimes.com/india-news/aicc-asks-youth-wing-to-target-230-lok-sabha-seats/story-96BpzQRZMs8nWhRpOJ3fdN_amp.html</ref>
}}
<references />
'''भारतीय युवा कांग्रेस''' इ [[भारतीय राष्ट्रीय काँग्रेस|भारतीय राष्ट्रीय कांग्रेस]] के युवा प्रकोष्ठ ह। भारतीय युवा कांग्रेस १९४७ में भारत के बंटवारा के ठीक बाद के समय से लेके १९६० के दशक के अंत तक भारतीय राष्ट्रीय कांग्रेस के एगो विंग रहे।
जबकि प्रधानमंत्री, [[इंदिरा गाँधी|इंदिरा गांधी]] युवा कांग्रेस के कांग्रेस पार्टी के फ्रंटल संगठन के रूप में स्थापित क के एगो नाया आयाम देली, जवना के मकसद रहे कि सामाजिक काम कइल अउरी दक्षिणपंथी दल के खिलाफ मोर्चा खोलल जाए। प्रियरंजन दासमुंशी भारतीय युवा कांग्रेस के पहिला चुनल अध्यक्ष रहलें; बाद में भारतीय मंत्रिमंडल में सूचना आ प्रसारण आ संसदीय मामिला के मंत्री बनलें।
4sbnarrvyms62bfefc576am33dlzpzb
चार्ल्स डी गॉल
0
88981
803365
766311
2026-07-29T16:23:57Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803365
wikitext
text/x-wiki
{{Infobox officeholder
| name = चार्ल्स डी गॉल
| image = De Gaulle-OWI (cropped)-(c).jpg
| caption = चार्ल्स डी गॉल १९४४ में
| office = [[फ्रांस के रास्ट्रपति]]
| predecessor = [[René Coty]]
| primeminister = {{plainlist|
* [[Michel Debré]]
* [[Georges Pompidou]]
* [[Maurice Couve de Murville]]}}
| successor = [[Georges Pompidou]]
| termstart = 8 January 1959
| termend = 28 April 1969
| office1 = [[Prime Minister of France]]
| president1 = [[René Coty]]
| termstart1 = 1 June 1958
| termend1 = 8 January 1959
| predecessor1 = [[Pierre Pflimlin]]
| successor1 = [[Michel Debré]]
| office2 = Chairman of the [[Provisional Government of the French Republic]]
| termstart2 = 3 June 1944
| termend2 = 26 January 1946
| predecessor2 = {{ubl|[[Philippe Pétain]]<br />(Chief of the French State) |[[Pierre Laval]]<br />(Chief of the Government) }}
| successor2 = [[Félix Gouin]]
| birth_name = Charles André Joseph Marie de Gaulle
| birth_date = {{birth date|1890|11|22|df=y}}
| birth_place = [[Lille]], France
| death_date = {{death date and age|1970|11|9|1890|11|22|df=y}}
| death_place = [[Colombey-les-Deux-Églises]], [[France]]
| resting_place = [[Colombey-les-Deux-Églises]], [[France]]
| party = [[Union of Democrats for the Republic]] (1967–1969)
| otherparty = [[Union for the New Republic (France)|Union for the New Republic]] (1958–1967)
| spouse = {{marriage|[[Yvonne de Gaulle|Yvonne Vendroux]]|1921}}
| children = {{hlist|[[Philippe de Gaulle|Philippe]]|Élisabeth|[[Anne de Gaulle|Anne]]}}
| alma_mater = [[École spéciale militaire de Saint-Cyr]]
| signature = Charles de Gaulle Signature 2.svg
| footnotes = {{notelist}}
| allegiance = {{plainlist|
* [[French Third Republic]]
* [[Free France]]}}
| branch = {{plainlist|
* [[French Army]]
* [[French Armed Forces]]
* [[Free French Forces]]}}
| serviceyears = 1912–1944
| rank = [[Brigadier general]]
| unit = {{plainlist|
* Infantry
* Armoured cavalry}}
| commands = {{plainlist|
* [[4e Division cuirassée]]
* [[Free France]]}}
| battles = {{Tree list}}
* [[World War I]]
** [[Battle of Verdun]]
** [[Battle of the Somme]]
* [[Polish–Soviet War]]
* [[World War II]]
** [[Battle of France]]
** [[Battle of Montcornet]]
** [[Battle of Abbeville]]
** [[Battle of Dakar]]
** [[Liberation of Paris]]
{{Tree list/end}}
}}'''चार्ल्स आंद्रे जोसेफ मैरी डी गॉल''' ;<ref>{{Citation|last=Wells|first=John C.|year=2008|title=Longman Pronunciation Dictionary|edition=3rd|publisher=Longman|isbn=978-1-4058-8118-0}}</ref> आमतौर पर एकरा के संक्षिप्त रूप से '''सीडीजी''' कहल जाला आ फ्रांस में एकरा के खाली " ''ले जनरल'' ", "द जनरल" के नाँव से जानल जाला; २२ नवंबर १८९० के भइल{{Spaced ndash}} ९ नवंबर १९७०) एगो फ्रांसीसी सेना के अधिकारी आ राज्यकर्त्ता रहलें जे [[दुसरा बिस्व जुद्ध]] में नाजी जर्मनी के खिलाफ फ्री फ्रांस के नेतृत्व कइलें आ [[फ्रांस]] में लोकतंत्र के बहाल करे खातिर १९४४ से १९४६ ले फ्रांसीसी गणराज्य के प्रोविजनल सरकार के अध्यक्ष रहलें। १९५८ में, ऊ रिटायरमेंट से बाहर निकललें जब राष्ट्रपति रेने कोटी द्वारा मंत्री परिषद (प्रधानमंत्री) के अध्यक्ष नियुक्त कइल गइल। उ फ्रांस के संविधान के दोबारा लिखले अउरी जनमत संग्रह से मंजूरी मिलला के बाद पांचवा गणराज्य के स्थापना कइले . ओह साल के अंत में ऊ फ्रांस के राष्ट्रपति चुनल गइलें, एह पद पर ऊ १९६५ में दोबारा चुनल गइलें आ १९६९ में इस्तीफा तक ले एह पद पर रहलें।
लिले में जनमल, ऊ १९१२ में सेंट-सिर से स्नातक कइलें। [[पहिला बिस्व जुद्ध|पहिला विश्वयुद्ध]] के सजावल अधिकारी रहले, कई बेर घायल भइल रहले अउरी बाद में वर्डन में बंदी बना लिहल गइल रहले . अंतरयुद्ध काल के दौरान, उ मोबाइल बख्तरबंद डिवीजन के वकालत कइले। मई १९४० के जर्मन आक्रमण के दौरान ऊ एगो बख्तरबंद डिवीजन के नेतृत्व कइलें जे आक्रमणकारी लोग पर जवाबी हमला कइलस; एकरा बाद उनुका के युद्ध के उपसचिव नियुक्त कइल गइल। जर्मनी के साथे आपन सरकार के युद्धविराम के स्वीकार करे से इनकार करत डी गॉल इंग्लैंड भाग गइले अउरी 18 जून के आपन अपील में फ्रांसीसी लोग के कब्जा के विरोध करे अउरी लड़ाई जारी रखे के आग्रह कइले| उ मुक्त फ्रांसीसी सेना के नेतृत्व कइले अउरी बाद में धुरी के खिलाफ फ्रांस के राष्ट्रीय मुक्ति समिति के नेतृत्व कइले . अमेरिका से ठंढा संबंध के बावजूद आम तौर प उनुका [[विंस्टन चर्चिल]] के समर्थन रहे अउरी उ फ्री फ्रांस के निर्विवाद नेता के रूप में उभरले . जून १९४४ में उ फ्रांसीसी गणराज्य के अस्थायी सरकार के प्रमुख बनले, फ्रांस के मुक्ति के बाद अंतरिम सरकार रहे| 1944 में ही डी गॉल एगो डिरिजिस्ट आर्थिक नीति शुरू कइलें जेह में पूंजीवादी अर्थब्यवस्था पर राज्य द्वारा निर्देशित पर्याप्त नियंत्रण सामिल रहल जेकरा बाद 30 साल ले अभूतपूर्व बिकास भइल जेकरा के ट्रेंटे ग्लोरियस के नाँव से जानल जाला। नयका चउथा गणराज्य में छोट-मोट पक्षपात के वापसी से निराश हो के ऊ १९४६ के सुरुआत में इस्तीफा दे दिहलें बाकी रासेम्बलमेंट डु पीपुल फ्रांसेज (RPF; "फ्रांसीसी लोग के रैली") के संस्थापक के रूप में राजनीतिक रूप से सक्रिय रहलें। १९५० के दशक के सुरुआत में ऊ रिटायर भइलें आ आपन ''युद्ध संस्मरण'' लिखलें जे जल्दीए आधुनिक फ्रेंच साहित्य के मुख्य हिस्सा बन गइल।
जब अल्जीरिया के युद्ध अस्थिर चउथा गणराज्य के फाड़त रहे त मई 1958 के संकट के दौरान राष्ट्रीय सभा उनुका के सत्ता में वापस ले आइल रहे . ऊ एगो मजबूत राष्ट्रपति पद के साथ पांचवा गणतंत्र के स्थापना कइलें, आ ऊ ओह भूमिका में जारी रहे खातिर चुनल गइलें। युद्ध के खतम करे खातिर कदम उठावत घरी ऊ फ्रांस के एक साथ रखे में कामयाब रहलें, जेकरा चलते ''पिड्स-नॉयर्स'' (अल्जीरिया में जनमल जातीय फ्रांसीसी) आ सशस्त्र बल लोग के बहुत नाराजगी भइल; दुनु जने पहिले औपनिवेशिक शासन बनवले राखे खातिर उनुका सत्ता में वापसी के समर्थन कइले रहले. ऊ [[अल्जीरिया]] के आजादी दिहलें आ फ्रांस के अउरी उपनिवेश सभ के प्रति प्रगतिशील रूप से काम कइलें। शीत युद्ध के संदर्भ में डी गॉल आपन "भव्यता के राजनीति" के सुरुआत कइलें, ई दावा कइलें कि फ्रांस के एगो प्रमुख ताकत के रूप में अपना राष्ट्रीय सुरक्षा आ समृद्धि खातिर अमेरिका नियर अउरी देस सभ पर भरोसा ना करे के चाहीं। एकरा खातिर उ "राष्ट्रीय स्वतंत्रता" के नीति के पालन कइले जवना के वजह से उ [[नाटो]] के एकीकृत सैन्य कमान से हट गइले अउरी एगो स्वतंत्र परमाणु हमला बल शुरू कइले जवना से फ्रांस दुनिया के चउथा परमाणु शक्ति बन गइल| ऊ २२ जनवरी १९६३ के एलिसी संधि पर हस्ताक्षर के माध्यम से एंग्लो-अमेरिकी आ सोवियत प्रभाव क्षेत्र सभ के बीच यूरोपीय प्रतिभार पैदा करे खातिर सौहार्दपूर्ण फ्रेंको-जर्मन संबंध सभ के बहाल कइलें।
डी गॉल एगो सुपरनेशनल यूरोप के कवनो विकास के विरोध कइले, यूरोप के संप्रभु राष्ट्र के महाद्वीप के रूप में पक्ष में रहले . डी गॉल वियतनाम में अमेरिका के हस्तक्षेप अउरी अमेरिका के डॉलर के " अत्यधिक विशेषाधिकार " के खुल के आलोचना कइले| बाद के सालन में " ''Vive le Québec libre'' " नारा के समर्थन आ यूरोपीय आर्थिक समुदाय में ब्रिटेन के प्रवेश के दू गो वीटो से उत्तरी अमेरिका आ यूरोप दुनों जगह काफी बिबाद पैदा भइल। हालांकि १९६५ में राष्ट्रपति पद खातिर फिर से चुनल गइले, लेकिन मई 1968 में छात्र अउरी मजदूर के ओर से व्यापक विरोध के सामना करे के पड़ल, लेकिन सेना के समर्थन मिलल अउरी राष्ट्रीय सभा में बढ़ल बहुमत के संगे चुनाव जीतले। डी गॉल १९६९ में जनमत संग्रह में हार के इस्तीफा दे दिहलें जवना में ऊ अउरी विकेंद्रीकरण के प्रस्ताव रखलें। एक साल बाद इनके निधन कोलंबई-लेस-ड्यूक्स-एग्लिसेस में इनके आवास पर भइल आ इनके राष्ट्रपति पद के संस्मरण अधूरा रहल।
कई गो फ्रांसीसी राजनीतिक दल आ नेता लोग गॉलवादी बिरासत के दावा करे ला; फ्रांस के कई गो गली आ स्मारक उनके निधन के बाद इनके याद में समर्पित कइल गइल।
hlkjyn0wjtftn0ugb3tej7n1l4vpe2x
शीत युद्ध
0
88989
803353
734477
2026-07-29T16:23:00Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803353
wikitext
text/x-wiki
{{Infobox}}
'''शीत जुद्ध''' एगो शब्द हवे जेकर परयोग आमतौर पर [[अमेरिका]] आ [[सोवियत यूनियन|सोवियत संघ]] आ इनहन के अपना-अपना सहयोगी देस सभ पच्छिमी ब्लॉक आ पूरबी ब्लॉक के बीच भूराजनीतिक तनाव के दौर के कहल जाला। ''शीत युद्ध'' शब्द के प्रयोग एह से कइल गइल बा काहे कि दुनो महाशक्ति के बीच सीधे कवनो बड़ पैमाना पऽ लड़ाई ना भइल रहे, लेकिन ई लोग हर एक प्रमुख क्षेत्रीय संघर्ष में विरोधी पक्ष के समर्थन कइले रहे जवना के प्रॉक्सी युद्ध के नाम से जानल जाला। ई संघर्ष एह दुनों महाशक्ति सभ द्वारा वैश्विक परभाव खातिर वैचारिक आ भूराजनीतिक संघर्ष पर आधारित रहल, १९४५ में नाजी जर्मनी आ शाही जापान के खिलाफ इनहन लोग के अस्थायी गठबंधन आ जीत के बाद परमाणु शस्त्रागार के विकास आ परंपरागत सैन्य तैनाती के अलावा, वर्चस्व के संघर्ष के अप्रत्यक्ष तरीका से व्यक्त कइल गइल जईसे कि मनोवैज्ञानिक युद्ध, प्रचार अभियान, जासूसी, दूरगामी नाकाबंदी, खेल आयोजन में प्रतिद्वंद्विता आ अंतरिक्ष दौड़ जइसन तकनीकी प्रतियोगिता।
पच्छिम ब्लॉक के नेतृत्व अमेरिका के साथे साथे कई गो अउरी पहिला दुनिया के रास्ट्र भी करत रहे जवन आम तौर पर उदार लोकतांत्रिक रहे लेकिन अक्सर निरंकुश राज्यन के नेटवर्क से जुड़ल रहे, जवना में से अधिकतर ओह लोग के पूर्व उपनिवेश रहे। {{Sfn|G. Jones|2014|pp=176–179}} {{Efn-ua|[https://www.economist.com/blogs/economist-explains/2013/11/economist-explains-16 "Where did banana republics get their name?" ''The Economist'', 21 November 2013]}} पूरबी ब्लॉक के नेतृत्व सोवियत संघ आ एकरे कम्युनिस्ट पार्टी द्वारा कइल गइल जेकर परभाव पूरा दुसरी दुनिया में रहल आ निरंकुश राज्य सभ के नेटवर्क से भी जुड़ल रहल। अमेरिकी सरकार पूरा दुनिया में कम्युनिस्ट आ दक्षिणपंथी विरोधी सरकार आ विद्रोह के समर्थन कइलस जबकि सोवियत सरकार दुनिया भर में वामपंथी दल आ क्रांति के फंडिंग कइलस जइसे-जइसे लगभग सभ औपनिवेशिक राज्य 1945 से 1960 के दौरान आजादी हासिल कइलें, शीत युद्ध में कई गो तीसरी दुनिया के लड़ाई के मैदान बन गइलें।
शीत युद्ध के पहिला चरण १९४५ में [[दुसरा बिस्व जुद्ध]] के खतम होखे के कुछ समय बाद नाँव। अमेरिका अउरी ओकर सहयोगी देस १९४९ में सोवियत हमला के आशंका में [[नाटो|नाटो के]] सैन्य गठबंधन बनवले रहे अउरी सोवियत प्रभाव के ''रोकथाम'' के खिलाफ आपन वैश्विक नीति के नाम देले रहे। सोवियत संघ नाटो के जवाब में १९५५ में वारसॉ पैक्ट के गठन कइले रहे| एह चरण के प्रमुख संकट सभ में १९४८–१९४९ के बर्लिन नाकाबंदी, १९४५–१९४९ के चीनी कम्युनिस्ट क्रांति, १९५०–१९५३ के कोरियाई युद्ध, १९५६ के हंगरी क्रांति, १९५६ के स्वेज संकट, १९६१ के बर्लिन संकट, १९६२ के क्यूबा मिसाइल संकट, आ... १९६४-१९७५ के वियतनाम युद्ध के बारे में बतावल गइल बा अमेरिका अउरी सोवियत संघ के बीच लैटिन अमेरिका, [[मध्य पूरुब|मध्य पूर्व]] अउरी अफ्रीका, एशिया अउरी ओशिनिया के विउपनिवेशीकरण राज्यन में प्रभाव खातिर होड़ लागल रहे।
क्यूबा के मिसाइल संकट के बाद एगो नया चरण शुरू भइल जवना में चीन आ सोवियत संघ के बीच चीन-सोवियत के बंटवारा कम्युनिस्ट क्षेत्र के भीतर संबंध के जटिल बना दिहलस, जवना के चलते सीमा पर कई गो मुठभेड़ के सिलसिला सुरू हो गइल, जबकि पच्छिमी ब्लॉक के एगो राज्य फ्रांस अउरी के मांग करे लागल कार्रवाई के स्वायत्तता के बारे में बतावल गइल बा। १९६८ के प्राग वसंत के दबावे खातिर सोवियत संघ चेकोस्लोवाकिया पर हमला कइलस जबकि अमेरिका के नागरिक अधिकार आंदोलन अउरी वियतनाम युद्ध के विरोध से आंतरिक उथल-पुथल के सामना करे के पड़ल। १९६०–१९७० के दशक में दुनिया भर के नागरिकन के बीच एगो अंतर्राष्ट्रीय शांति आंदोलन जड़ जमा लिहलस। परमाणु हथियार के परीक्षण के बिरूद्ध अउरी परमाणु निरस्त्रीकरण खातिर आंदोलन भइल, जवना में युद्ध विरोधी बड़हन विरोध प्रदर्शन भइल | १९७० के दशक ले दुनों पक्ष शांति आ सुरक्षा खातिर भत्ता देवे शुरू क देले रहलें आ एह से डिटेन्ट के दौर के सुरुआत भइल जेह में सामरिक हथियार सीमा वार्ता आ अमेरिका [[चीन|के पीपुल्स रिपब्लिक ऑफ चीन]] के साथ संबंध खोलल सोवियत संघ के सामरिक प्रतिकार के रूप में देखल गइल। १९७० के दसक के दूसरा छमाही में अंगोला, मोजाम्बिक, इथियोपिया, कंबोडिया, अफगानिस्तान आ निकारागुआ समेत कई गो स्वघोषित मार्क्सवादी–लेनिनवादी सरकार के गठन भइल।
दशक के अंत में १९७९ में सोवियत-अफगान युद्ध के सुरुआत के साथ डिटेन्ट के पतन हो गइल। 1980 के दशक के शुरुआत एगो अउरी दौर रहे जवना में तनाव बढ़ल रहे| अमेरिका सोवियत संघ पर कूटनीतिक, सैन्य अउरी आर्थिक दबाव बढ़ा दिहलस, ओह समय जब ऊ पहिले से ही आर्थिक ठहराव से ग्रस्त रहे। 1980 के दशक के बीच में, नयका सोवियत नेता [[मिखाइल गोर्बाचोभ|मिखाइल गोरबाचेव]] ''ग्लासनोस्ट'' ("खुलापन", लगभग १९८५) आ ''पेरेस्ट्रोइका'' ("पुनर्गठन", १९८७) के उदारीकरण करे वाला सुधार के सुरुआत कइलें आ १९८९ में अफगानिस्तान में सोवियत सामिल के खतम कइलें। पूरबी यूरोप में राष्ट्रीय संप्रभुता के दबाव अउरी मजबूत हो गइल आ गोरबाचेव अब ओह लोग के सरकारन के सैन्य समर्थन देबे से मना कर दिहलन।
१९८९ में पैन-यूरोपीय पिकनिक के बाद लोहा के पर्दा के गिरल आ क्रांति के शांतिपूर्ण लहर ( रोमानिया आ अफगानिस्तान के छोड़ के) पूरबी ब्लॉक के लगभग सगरी सरकार सभ के उखाड़ फेंक दिहलस। सोवियत संघ के कम्युनिस्ट दल खुद एह देस पर नियंत्रण खो दिहलस आ अगस्त १९९१ में गर्भपात करावे वाला तख्तापलट के कोशिश के बाद एकरा पर रोक लगा दिहल गइल। एकरे बाद दिसंबर १९९१ में सोवियत संघ के औपचारिक बिघटन भइल, एकरे घटक गणराज्य सभ के आजादी भइल आ अफिरका आ एशिया के बहुत सारा हिस्सा में साम्यवादी सरकार सभ के पतन भइल। अमेरिका के दुनिया के एकमात्र महाशक्ति के रूप में छोड़ दिहल गइल।
शीत जुद्ध आ एकर घटना एगो महत्वपूर्ण विरासत छोड़ले बा। एकरा के अक्सर लोकप्रिय संस्कृति में संदर्भित कइल जाला, खासतौर पर जासूसी आ परमाणु युद्ध के खतरा के बिसय सभ के साथ। बाद के इतिहास खातिर 1989 के बाद से अंतर्राष्ट्रीय संबंध देखल जाय।
326vc5jtvl5fvpjk1aabxw3ljwdg9i2
मनुस्मृति
0
88990
803292
754541
2026-07-29T16:06:15Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803292
wikitext
text/x-wiki
{{हिंदू धर्म}}
'''मनुस्मृति''' [[हिंदू धर्म]] के एगो प्राचीन धर्मशास्त्र (स्मृति) हऽ। ई पहिला संस्कृत ग्रंथ सभ में से एक रहल जे 1776 में अंग्रेजी में अनुवाद कइल गइल, ब्रिटिश भाखा बिज्ञानी सर विलियम जोन्स द्वारा<ref name="Flood 1996, page 56">{{Cite web|url=https://archive.org/details/AnIntroductionToHinduismSeeTantraWesternPhilosophyAscetismFoldersGavinFloodOUP/page/n70|title=Flood (1996), page 56}}</ref> आ एकर इस्तेमाल ब्रिटिश औपनिवेशिक सरकार के लाभ खातिर हिंदू कानून बनावे में कइल गइल। मनुस्मृति में कुल 12 गो अध्याय बा जवना में 2684 श्लोक बा। कुछ संस्करणन में श्लोक के संख्या 2964 बा।
एकरा के दुनिया के अइसन पुस्तकन में गिनल जाला, जवना से मनुष्य व्यक्तिगत आचरण आ सामाजिक गठन खातिर प्रेरणा लेले बा। एह में प्रश्न खाली धार्मिक आस्था भा आस्था के नइखे। हर सामाजिक व्यक्ति के ई अपेक्षा आ आकांक्षा होला कि मानव जीवन के जरूरत के कवनो भी तरीका से आपसी सहयोग से आ सुरुचिपूर्ण तरीका से पूरा कइल जा सकेला। बिदेस में एह बिसय पर पर्याप्त शोध भइल बा, तुलनात्मक अध्ययन भइल बा आ आलोचना भी भइल बा। हिन्दू समाज में एकर स्थान [[वेद|वेदत्रायी]] के बाद बा। मनुस्मृति के पचास गो पाण्डुलिपि मिलल बा। समय के साथ कई गो प्रक्षेप भी स्वाभाविक होला। बाद में जवन हिस्सा जोड़ल गइल बा ओकर पहचान कवनो आम आदमी खातिर संभव नइखे। तुलनात्मक अध्ययन के बाद ई काम खाली आधिकारिक विद्वान कर सकेला।
=== भारत से बाहर प्रभाव===
एंटनी रीड<ref>'''Anthony Reid''' (1988), Southeast Asia in the Age of Commerce, 1450-1680: The lands below the winds, Yale University Press, ISBN 978-0300047509, pages 137-138</ref> के कहनाम बा कि [[म्याँमार|बर्मा]], [[थाइलैंड|थाईलैंड]], [[कंबोडिया]], जावा- बाली आदि में धार्मिक ग्रंथ आ मुख्य रूप से मनुस्मृति के बहुत सम्मान रहे। एह देस सभ में एह ग्रंथ सभ के प्राकृतिक नियम के ग्रंथ मानल जात रहे आ राजा लोग से इनहन के अनुसार काम करे के उमेद कइल जात रहे। एह ग्रंथन के नकल, अनुवाद आ स्थानीय कानून में शामिल कइल गइल।<ref>Victor Lieberman (2014), Burmese Administrative Cycles, Princeton University Press, ISBN 978-0691612812, pages 66-68; Also see discussion of 13th-century Wagaru Dhamma-sattha / 11th century Manu Dhammathat manuscripts discussion</ref><ref>On Laws of Manu in 14th-century Thailand's Ayuthia kingdom named after Ayodhya, see David Wyatt (2003), Thailand: A Short History, Yale University Press, ISBN 978-0300084757, page 61;
Robert Lingat (1973), The Classical Law of India, University of California Press, ISBN 978-0520018983, pages 269-272</ref>
लुईस जैकोलियट ‘भारत में बाइबिल’ पुस्तक लिखले बाड़न कि:
: ''मनुस्मृति ऊ आधारशिला हऽ जवना पर [[मिस्र|मिस्र के]], फारसी, [[यूनान|ग्रेसियन]] आ [[रोम|रोमन]] कानूनी संहिता के निर्माण भइल रहे। आज भी मनु के प्रभाव यूरोप में महसूस कइल जा सकेला।''
: ''(मनुस्मृति ऊ आधार रहल जवना पर मिस्र, फारसी, यूनानी आ रोमन कानून संहिता के निर्माण भइल आ यूरोप में मनु के परभाव आजुओ महसूस कइल जाला।)<ref>V. Krishna Rao. Expansion of Cultural Imperalism Through Globalisation. Manak Publications. p. 82.</ref>''
{{clear}}
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:हिंदू ग्रंथ]]
{{Hinduism-stub}}
6rnp87ee74no0lg9tf1fpf4xslfw62t
मध्य प्रदेश के जिला सभ के लिस्ट
0
88991
803308
734496
2026-07-29T16:19:50Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803308
wikitext
text/x-wiki
[[चित्र:Madhya_Pradesh_district_map.svg|thumb|500x500px|{{Legend|pink|[[Chambal division]]}}{{Legend|green|[[Gwalior division]]}}{{Legend|darkorange|[[Bhopal division]]}}{{Legend|blue|[[Ujjain division]]}}{{Legend|hotpink|[[Indore division]]}}{{Legend|skyblue|[[Narmadapuram division]]}}{{Legend|red|[[Sagar division]]}}{{Legend|turquoise|[[Rewa division]]}}{{Legend|purple|[[Shahdol division]]}}{{Legend|gold|[[Jabalpur division]]}}]]
[[भारत के राज्य अउरी संघ राज्यक्षेत्र|भारत]] के [[मध्य प्रदेश]] राज्य 1 नवम्बर 1956 से अस्तित्व में आइल। मध्य प्रदेश में बिबिध भौगोलिक क्षेत्र बाड़ें जिनहन के कौनों आधिकारिक प्रशासनिक सरकारी दर्जा नइखे; कुछ ऐतिहासिक देस, राज्य भा प्रांत सभ से मेल खाला। वर्तमान में राज्य में जिला के संख्या 52 बाटे।<ref>{{Cite web|url=https://mpdistricts.nic.in/|title=DISTRICTS & DIVISIONS OF MADHYA PRADESH|website=District Portal of Madhya Pradesh|publisher=[[मध्य प्रदेश सरकार]]|access-date=9 अप्रैल 2021}}</ref> एह जिला सभ के दस गो प्रशासनिक बिभाजन में बाँटल गइल बा। जिला के तहसील में उपविभाजित कइल गइल बा, जवना में से मध्य प्रदेश में 428 बा।
{{clear}}
== संदर्भ ==
{{Reflist|33em}}
[[श्रेणी:मध्य प्रदेश]]
eo1zzc28qcnu14chjdi0n9870t5uavq
सोवियत संघ के बिघटन
0
89253
803348
783771
2026-07-29T16:22:35Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803348
wikitext
text/x-wiki
{{Infobox event|title=सोवियत संघ के बिघटन|image_name=|Imagesize=300px|image_alt=The Soviet flag being lowered from the Moscow Kremlin and replaced with the flag of Russia|caption=[[सोवियत संघ के झंडा|सोवियत लाल बैनर]] के [[मास्को क्रेमलिन|क्रेमलिन]] से उतारल गइल आ २५ दिसंबर १९९१ के [[रूसी तिरंगा]] से बदल दिहल गइल, [[मिखाइल गोरबाचेव]] के घोषणा के कुछ पल बाद उनकर इस्तीफा, [[बेलोवेझा समझौता]] के मान्यता देत।|participants={{bulleted list|[[Demographics of the Soviet Union#Ethnic groups|Nations]] of the Soviet Union|[[Government of the Soviet Union|Soviet government]]|Governments of the [[republics of the Soviet Union|republics]]|Governments of the [[Autonomous Soviet Socialist Republics|autonomous republics]]|[[Nationalism|Nationalist]] and [[liberalism|liberal]] opposition}}|date={{nowrap|{{start and end dates|१९८८|११|१६|१९९१|१२|२६|df=y}}}}<br />(३ साल,१ महिना अउर १० दिन)|Location={{Collapsible list
|title=[[Soviet Union]]
|bullets=on
|'''[[Post-Soviet states|former]] [[Republics of the Soviet Union|Soviet republics]]''':<br>{{nowrap|{{flagicon|Armenian SSR}}→{{flag|Armenia}}}}<br>{{nowrap|{{flagicon|Azerbaijan SSR}}→{{flag|Azerbaijan}}}}<br>{{nowrap|{{flagicon|Byelorussian SSR}}→{{flag|Belarus|1991}}{{ref label|a|a}}}}<br>{{nowrap|{{flagicon|Estonian SSR}}→{{flag|Estonia}}}}<br>{{nowrap|{{flagicon|Georgian SSR}}→{{flag|Georgia|1990}}}}<br>{{nowrap|{{flagicon|Kazakh SSR}}→{{flag|Kazakhstan}}}}<br>{{nowrap|{{flagicon|Kirghiz SSR}}→{{flag|Kyrgyzstan}}{{ref label|b|b}}}}<br>{{nowrap|{{flagicon|Latvian SSR}}→{{flag|Latvia}}}}<br>{{nowrap|{{flagicon|Lithuanian SSR}}→{{flag|Lithuania|1988}}}}<br>{{nowrap|{{flagicon|Moldavian SSR}}→{{flag|Moldova}}{{ref label|c|c}}}}<br>{{nowrap|{{flagicon|Russian SFSR}}→{{flag|Russia|1991}}}}<br>{{nowrap|{{flagicon|Tajik SSR}}→{{flag|Tajikistan|1991}}}}<br>{{nowrap|{{flagicon|Turkmen SSR}}→{{flag|Turkmenistan|1991}}{{ref label|d|d}}}}<br>{{nowrap|{{flagicon|Ukrainian SSR}}→{{flagicon image|Flag of Ukraine (Soviet shades).svg}} [[Ukraine]]}}<br>{{nowrap|{{flagicon|Uzbek SSR}}→{{flag|Uzbekistan}}}}
|'''unrecognized breakaway states''':<br>{{flagicon image|Flag of Artsakh.svg|link=Republic of Artsakh}} [[Republic of Artsakh|Artsakh]] {{small|(1991–present)}}<br>{{flagicon image|Flag of the Chechen Republic (before 2004).svg|link=Chechen Republic}} [[Chechen Republic of Ichkeria#Declaration of independence|Chechnya]] {{small|(1991–[[Second Chechen War|2000]])}}<br>{{flagicon image|Flag of the Gagauz people.svg|link=Gagauz Republic}} [[Gagauz Republic|Gagauzia]] {{small|(1990–1994)}}<br>{{flagicon image|Flag of Azerbaijan.svg|link=Nakhchivan Autonomous Republic}} [[Nakhchivan Autonomous Republic|Nakhchivan]] {{small|(1990–1993)}}<br>{{flagicon|Tatarstan}} [[Tatarstan#Present-day|Tatarstan]] {{small|(1992–1994)}}<br>{{flagicon|Transnistria|state}} [[Transnistria]] {{small|(1990–present)}}
}}|result={{bulleted list|[[Dissolution (law)|Dissolution]] of the Soviet Union into [[post-Soviet states|fifteen independent states]]||Establishment of the [[Commonwealth of Independent States]] between eleven independent states|[[Post-Soviet states#Separatist conflicts|Several separatist movements]] in the former [[Autonomous Soviet Socialist Republics|autonomies]] prove successful, most either fail to combat the militaries of their respective republics or agree to rejoin them peacefully|Numerous [[Post-Soviet conflicts|military conflicts]] and ethnic clashes unfold during and after the dissolution, provoking humanitarian crises and leaving many [[internally displaced person|internally displaced]]|Republics declare [[multi party system|multi-party]] [[Presidential republic|presidential]] or [[semi-presidential republic|semi-presidential]] systems|Republics move to adopt [[capitalism|capitalist]] [[market economy]]|[[Soviet ruble|Ruble]] zone active in most of the new states in 1992–94 with national currencies adopted later|Unified [[Soviet Armed Forces]] divided circa 1992–1993|[[Black Sea Fleet]] [[Partition Treaty on the Status and Conditions of the Black Sea Fleet|partitioned]] and negotiation for relocation between Russia and Ukraine in 1997 until 2017; [[Annexation of Crimea by the Russian Federation|terminated in 2014]]|Issues with value loss of savings of former Soviet citizens|Issues with social and medical support for veterans of the [[Soviet–Afghan War]]|Relocation of the [[Soviet Armed Forces]] from [[East Germany]] and the rest of [[Central Europe]]|End of the [[Cold War]]|End of [[Communist Party of the Soviet Union|CPSU]] rule|Decline of [[communism|communist]] movements around the world|The [[China|People's Republic of China]] becomes the last major communist state in the world following [[Chinese economic reforms|economic reforms]]|The [[United States]] becomes the world's sole [[superpower]]
}}|partof=[[शीत युद्ध]] आ [[१९८९ के क्रांति]]|notes=<div style="text-align: left">{{nowrap|{{note|a}} Then romanized as ''Byelorussia'' ({{lang-ru|Белоруссия}}).}}<br>{{note|b}} Then romanized as ''Kirghizia'' ({{lang-ru|Киргизия}}).<br>{{note|c}} Then romanized as ''Moldavia'' ({{lang-ru|Молдавия}}).<br>{{note|d}} Then romanized as ''Turkmenia'' ({{lang-ru|Туркмения}}).</div>}}'''सोवियत संघ के बिघटन''' {{Efn|{{lang-rus|Распа́д Сове́тского Сою́за|r=Raspád Sovétskogo Soyúza}}, also negatively connoted as {{lang-rus|Разва́л Сове́тского Сою́за|r=Razvál Sovétskogo Soyúza}}, ''Ruining of the Soviet Union''.}} [[सोवियत यूनियन|सोवियत संघ]] (USSR) के भीतर आंतरिक बिघटन के प्रक्रिया रहल जेकर परिणाम ई भइल कि एह देस आ एकरे संघीय सरकार के अस्तित्व के एगो संप्रभु राज्य के रूप में खतम हो गइल आ एकरे परिणामस्वरूप एकरे घटक गणराज्य सभ के फायदा भइल 26 दिसंबर 1991 के पूरा आजादी मिलल । एकरा से महासचिव (बाद में राष्ट्रपति भी ) [[मिखाइल गोर्बाचोभ|मिखाइल गोरबाचेव]] के सोवियत राजनीतिक अउरी आर्थिक व्यवस्था में सुधार के प्रयास के अंत हो गइल जवना से राजनीतिक गतिरोध अउरी आर्थिक पश्चाताप के दौर के रोके के कोशिश कइल गइल| सोवियत संघ में आंतरिक ठहराव अउरी जातीय अलगाववाद के अनुभव भईल रहे| हालाँकि, अंतिम साल ले ई देस बहुत केंद्रीकृत रहल, पंद्रह गो शीर्ष स्तर के गणराज्य सभ से बनल रहल जे अलग-अलग जातीयता सभ खातिर वतन के रूप में काम करत रहलें। १९९१ के अंत तक एगो विनाशकारी राजनीतिक संकट के बीच कई गो गणराज्य संघ से बाहर निकल चुकल रहे अउरी केंद्रीकृत सत्ता के कम होखे के बीच एकर तीन संस्थापक सदस्यन के नेता लोग घोषणा कइल कि सोवियत संघ के अस्तित्व नइखे रह गइल | एकरा कुछ समय बाद आठ गो अउरी गणराज्य ओह लोग के घोषणा में शामिल हो गइल । दिसंबर १९९१ में गोरबाचेव इस्तीफा दे दिहलें आ सोवियत संसद से जवन कुछ बचल रहे ऊ खुदे खतम करे के वोट दिहलस।
ई प्रक्रिया संघ के बिबिध घटक राष्ट्रीय गणराज्य सभ में बढ़त अशांति से शुरू भइल जे इनहन आ केंद्र सरकार के बीच लगातार राजनीतिक आ बिधायी संघर्ष के रूप में बिकसित भइल। एस्टोनिया पहिला सोवियत गणराज्य रहल जे १६ नवंबर १९८८ के संघ के भीतर राज्य संप्रभुता के घोषणा कइलस । लिथुआनिया पहिला गणराज्य रहल जे 11 मार्च 1990 के अधिनियम से सोवियत संघ से पूरा आजादी के घोषणा कइलस आ एकरे बाल्टिक पड़ोसी आ [[पारकॉकेशस|दक्खिनी काकेशस]] गणराज्य [[जॉर्जिया (देश)|जॉर्जिया]] दू महीना के दौरान एकरा में शामिल हो गइलें।
अगस्त १९९१ में कम्युनिस्ट कट्टरपंथी अउरी सैन्य अभिजात वर्ग गोरबाचेव के उखाड़ फेंके अउरी असफल सुधार के तख्तापलट में रोके के कोशिश कइलस, लेकिन असफल हो गइल| एह उथल-पुथल के कारण मास्को में सरकार के ज्यादातर परभाव खतम हो गइल आ एकरे बाद के दिन आ महीना सभ में कई गणराज्य सभ आजादी के घोषणा कइलें। बाल्टिक राज्यन के अलगाव के सितंबर १९९१ में मान्यता दिहल गइल। बेलोवेझा समझौता पर ८ दिसंबर के [[रूस]] के राष्ट्रपति बोरिस येल्त्सिन, [[यूक्रेन]] के राष्ट्रपति क्रावचुक, आ [[बेलारूस]] के अध्यक्ष शुशकेविच द्वारा हस्ताक्षर कइल गइल आ एक दुसरा के आजादी के मान्यता दिहल गइल आ सोवियत संघ के जगह स्वतंत्र राज्य सभ के राष्ट्रमंडल (CIS) के निर्माण कइल गइल। [[कजाकिस्तान]] संघ से बाहर निकले वाला आखिरी गणराज्य रहल आ १६ दिसंबर के आजादी के घोषणा कइलस। जॉर्जिया आ बाल्टिक राज्यन के छोड़ के सगरी पूर्व सोवियत गणराज्य २१ दिसंबर के सीआईएस में शामिल भइलें आ अल्मा-आटा प्रोटोकॉल पर हस्ताक्षर कइलें। २५ दिसंबर के गोरबाचेव इस्तीफा दे दिहलें आ आपन राष्ट्रपति के अधिकार – जवना में परमाणु प्रक्षेपण कोड पर नियंत्रण भी शामिल रहे – येल्त्सिन के सौंप दिहलें जे अब [[रूस|रूसी संघ]] के पहिला राष्ट्रपति रहलें। ओह दिन साँझ के क्रेमलिन से सोवियत झंडा उतार के ओकरा जगह पर रूस के तिरंगी झंडा लगावल गइल। अगिला दिने सोवियत संघ के ऊपरी कक्ष के सुप्रीम सोवियत गणतंत्र के सोवियत संघ के औपचारिक रूप से भंग कर दिहलस । <ref>
{{Cite news|url=https://www.nytimes.com/1991/12/22/world/end-soviet-union-text-declaration-mutual-recognition-equal-basis.html|title=The End of the Soviet Union; Text of Declaration: 'Mutual Recognition' and 'an Equal Basis'|date=22 December 1991|work=The New York Times|access-date=30 March 2013}}
</ref> विघटन के घटना 1989 के क्रांति के प्रमुख निष्कर्ष अउरी शीत युद्ध के अंत भी रहे। <ref>{{Cite news|url=https://www.bloomberg.com/opinion/articles/2021-12-23/30-years-later-soviet-union-s-collapse-still-has-lessons-for-china|title=For China, USSR's 1991 Collapse is Still News It Can Use|date=23 December 2021|work=Bloomberg.com|access-date=4 January 2023|archive-url=https://web.archive.org/web/20230107200259/https://www.bloomberg.com/opinion/articles/2021-12-23/30-years-later-soviet-union-s-collapse-still-has-lessons-for-china|archive-date=7 January 2023}}</ref>
शीत युद्ध के बाद, पूर्व सोवियत गणराज्य सभ में से कई गो रूस से गहिराह संबंध बनवले रखले बाड़ें आ बहुपक्षीय संगठन सभ के गठन कइले बाड़ें जइसे कि CIS, सामूहिक सुरक्षा संधि संगठन (CSTO), यूरेशियन आर्थिक संघ (EAEU), आ संघ राज्य, आर्थिक आ सैन्य सहयोग खातिर दिहल गइल बा. दूसर ओर बाल्टिक राज्य आ वारसॉ संधि के पहिले के अधिकतर राज्य [[यूरोपियन यूनियन|यूरोपीय संघ]] (EU) के हिस्सा बन गइलें आ [[नाटो]] में शामिल भइलें जबकि कुछ अउरी पूर्व सोवियत गणराज्य जइसे कि यूक्रेन, जॉर्जिया आ [[मोल्डोवा|मोल्दोवा]] सार्वजनिक रूप से फॉलो करे में रुचि जतवले बाड़ें १९९० के दशक के बाद से इहे रास्ता बा।
ba0mq9db1vc6g914vjc6uco1cikls7h
ट्राईस्काईडेकाफोबियआ
0
89266
803312
734640
2026-07-29T16:20:06Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803312
wikitext
text/x-wiki
[[File:P2110024NoThirteenStaAnita_wb.jpg|thumb|सांता अनिता पार्क में स्टॉल के नंबर १२ से १२ए से १४ हो जाला।]]
'''त्रिस्काइडकाफोबिया''' ( {{IPAc-en|ˌ|t|r|ɪ|s|k|aɪ|ˌ|d|ɛ|k|ə|ˈ|f|oʊ|b|i|ə}} {{respell|TRIS|kye|DEK|ə|FOH|bee-ə}}, {{IPAc-en|ˌ|t|r|ɪ|s|k|ə|-}} {{respell|TRIS|kə-}} ; {{ety|grc|''τρεισκαίδεκα'' (treiskaídeka)|thirteen|grc|''φόβος'' (phóbos)|fear}} ) <ref>{{Cite web|url=https://www.etymonline.com/word/triskaidekaphobia|title=triskaidekaphobia - Origin and meaning of triskaidekaphobia by Online Etymology Dictionary|website=Etymonline.com|access-date=5 November 2017}}</ref> {{num|13}} संख्या से डर भा परहेज हवे। इहो एगो कारण बा शुक्रवार 13 तारीख के डर के, जेकरा के ''पैरास्केविडेकैट्रिआफोबिया'' ( {{ety|gre|''Παρασκευή'' (Paraskevi)|Friday|gre|''δεκατρείς'' (dekatreís)|thirteen|grc|''φόβος'' (phóbos)|fear}} कहल जाला के नाम से जानल जाला। के बारे में जानकारी दिहल गइल बा। ) या ''फ्रीग्गाट्रिस्काइडकाफोबिया'' ( {{ety|non|Frigg|[[Frigg]]|grc|''τρεισκαίδεκα'' (treiskaídeka)|thirteen|grc|''φόβος'' (phóbos)|fear}} १९९९ के बा। )।
एह शब्द के प्रयोग १९१० में ही इसाडोर कोरियाट द्वारा ''असामान्य मनोविज्ञान'' में कइल गइल रहे। <ref>"[https://books.google.com/books?id=Sio_NN91IqYC&printsec=frontcover&dq=Abnormal+psychology,+by+Isador+H.+Coriat.#v=onepage&q=&f=false Abnormal Psychology]" p. 319, published in 1910, Moffat, Yard and company (New York). Library of Congress Control No. [http://lccn.loc.gov/10011167 10011167].</ref>
c66noige3y9wcr1gecjjf88n5kvr7kw
अनिल चौहान
0
89502
803335
786968
2026-07-29T16:21:38Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803335
wikitext
text/x-wiki
{{Infobox officeholder
| image = General Anil Chauhan.jpg
| office = भारत के डिफेस्न्स स्टॉफ के प्रमुख
| predecessor = [[बिपिन रावत]]
| 1blankname = रक्षा मन्त्री
| 1namedata = [[राजनाथ सिंह]]
| president = [[दौपदी मुर्मू]]
| primeminister = [[नरेन्द्र मोदी]]
| office1 = 58वां [[चीफ ऑफ स्टाफ कमेटी के अध्यक्ष]]
| 1blankname1 = रक्षा मंत्री
| 1namedata1 = [[राजनाथ सिंह]]
| president1 = [[द्रौपदी मुर्मू]]
| primeminister1 = [[नरेंद्र मोदी]]
| predecessor1 = [[Manoj Mukund Naravane]] (Acting)
| successor1 =
| office2 = [[पूर्वी कमान (भारत)|जनरल ऑफिसर कमांडिंग-इन-चीफ पूर्वी कमान]]
| 1blankname2 = [[Chief of the Army Staff (India)|Chief of Army Staff]]
| 1namedata2 = {{plainlist|
* [[बिपिन रावत]]
* [[मनोज मुकुन्द नरवणे]]
}}
| predecessor2 = [[मनोज मुकुन्द नरवणे]]
| successor2 = [[मनोज पाण्डे]]
| birth_date = {{birth date and age|1961|05|18|df=y}}
| death_date = <!-- {{Death date and age|YYYY|MM|DD|YYYY|MM|DD}} death date first, then birth date -->
| birth_place = गवाना, [[पौरी गढ़वाल जिला]], [[उत्तर प्रदेश]], [[भारत]]<br /> (अब [[उत्तराखंड]], भारत में)
| death_place =
| nickname =
| birth_name =
| spouse = अनुपमा चौहान
| children = 1
| allegiance = {{IND}}
| branch = {{army|India}}
| serviceyears = जून 1981 {{endash}} मई 2021<br>2022 {{endash}} वर्तमान (सीडीएस के रूप में)
| rank = [[फ़ाइल: भारत CDS.svg|23px के लिए रैंक प्रतीक चिन्ह]] [[सामान्य (भारत)|सामान्य]]
| military_blank1 = [[Service number]]
| military_data1 = IC-39492A
| unit = [[फाइल: 11वीं गोरखा राइफल्स इन्सिग्निया (इंडिया).svg|20पीएक्स]] [[11वीं गोरखा राइफल्स]]
| commands = {{plainlist|
*[[फ़ाइल:आईए पूर्वी कमान.jpg|20px]] [[पूर्वी कमांड (भारत)|पूर्वी कमान]]
*[[फाइल: स्पीयर कॉर्प्स.jpg|20पीएक्स]] [[III कॉर्प्स (इंडिया)|III कॉर्प्स]]
}}
| battles =
| alma_mater = {{plainlist|
* [[राष्ट्रीय रक्षा अकादमी (भारत)|राष्ट्रीय रक्षा अकादमी, खड़गवासला]]
* [[भारतीय सैन्य अकादमी]], [[देहरादून]]
}}
| website =
| signature =
| signature_alt =
}}'''जनरल अनिल चौहान''' (जनम 18 मई 1961) वर्तमान में भारतीय सशस्त्र बल के चीफ ऑफ डिफेंस स्टाफ (CDS) बाड़ें <ref name="Bio">{{Cite web|url=https://www.aninews.in/news/national/general-news/lt-general-anil-chauhan-retd-appointed-as-new-chief-of-defence-staff20220928190853/|title=Lt General Anil Chauhan (retd) appointed as new Chief of Defence Staff|date=28 September 2022|website=www.aninews.in|access-date=7 March 2023|archive-date=29 November 2022|archive-url=https://web.archive.org/web/20221129072154/https://www.aninews.in/news/national/general-news/lt-general-anil-chauhan-retd-appointed-as-new-chief-of-defence-staff20220928190853/|url-status=dead}}</ref><ref>{{Cite web|url=https://www.aajtak.in/india/news/story/government-appoints-retired-lt-general-anil-chauhan-as-chief-of-defence-staff-cds-ntc-1546145-2022-09-28|title=रिटायर्ड लेफ्टिनेंट जनरल अनिल चौहान होंगे देश के नए CDS|date=2022-09-28|website=आज तक|language=hi|access-date=2022-10-04}}</ref> ऊ [[भारतीय सेना]] के फोर स्टार जनरल आ रिटायर्ड ऑफिसर बाड़ें। 30 सितंबर 2022 के इनके रक्षा प्रमुख बनावल गइल <ref>{{Cite web|url=https://www.indiatoday.in/india/story/general-anil-chauhan-takes-charge-as-india-new-chief-of-defence-staff-2006590-2022-09-30|title='Will tackle all challenges': General Anil Chauhan takes charge as Chief of Defence Staff|last=N|last2=DelhiSeptember 30|first2=ini Singh New|website=India Today|language=en|access-date=2022-10-07|last3=September 30|first3=2022UPDATED:|last4=Ist|first4=2022 11:11}}</ref><ref>{{Cite web|url=https://www.aajtak.in/india/news/story/general-anil-chauhan-takes-over-as-cds-pays-tribute-at-national-war-memorial-ntc-1547145-2022-09-30|title=जनरल अनिल चौहान ने संभाला सीडीएस का पदभार, कहा- सभी चुनौतियों का सामना करने को तैयार|date=2022-09-30|website=आज तक|language=hi|access-date=2022-10-07}}</ref> । उ परम विशिष्ठ सेवा पदक, उत्तम युद्ध सेवा पदक, अति विशिष्ठ सेवा पदक, सेवा पदक, विशिष्ठ सेवा पदक के प्राप्तकर्ता हवें।
जनरल अनिल चौहान अपना कैरियर में 40 साल से अधिका के कई गो कमान संभाल चुकल बाड़े। उनुका लगे जम्मू-कश्मीर अउरी पूर्वोत्तर भारत में आतंकवाद निरोधक अभियान के अनुभव भी बा।
== '''शुरुआती जिनगी आ शिक्षा''' ==
लेफ्टिनेंट जनरल अनिल चौहान के जनम 18 मई 1961 के उत्तराखंड (तब के उत्तर प्रदेश) के पौरी गढ़वाल जिला के ग्वाना में भइल रहे <ref>{{Cite web|url=https://www.aajtak.in/topic/lt-gen-anil-chauhan|title=Lt Gen Anil Chauhan Latest News, Updates in Hindi {{!}} लेफ्टिनेंट जनरल अनिल चौहान के समाचार और अपडेट - AajTak|website=आज तक|language=hi|access-date=2022-10-04}}</ref> । चौहान आपन शुरुआती स्कूली पढ़ाई कोलकाता के केंद्र विद्यालय फोर्ट विलियम से कइले रहले. बाद में ऊ राष्ट्रीय रक्षा अकादमी, खड़कवासला आ भारतीय सैन्य अकादमी, देहरादून (लेफ्टिनेंट जनरल अनिल चौहान एजुकेशन) से स्नातक कइलें। चौहान के बियाह अनुपमा से भईल बा, जवन कि एगो कलाकार (लेफ्टिनेंट जनरल अनिल चौहान पत्नी) हई। इनकर एगो बेटी बाड़ी प्रज्ञा।
== '''सेना में सेवा''' ==
चौहान के चार दशक के सैन्य कैरियर में 1981 में 11वीं गोरखा राइफल्स में शामिल कइल गइल रहे। चौहान मेजर जनरल के रूप में बारामुल्ला में उत्तरी कमान के एगो पैदल सेना डिवीजन के कमान संभालले रहले, बाद में लेफ्टिनेंट जनरल के रूप में 2017 से 2018 तक [[नागालैंड]] स्थित III कोर के कमांडिंग ऑफिसर के रूप में काम कइले। जनवरी 2018 में इनके महानिदेशक सैन्य अभियान (डीजीएमओ) नियुक्त कइल गइल, एह दौरान ऊ दू गो प्रमुख सैन्य अभियान सभ के निष्पादन के देखरेख कइलें – [[पाकिस्तान]] के खिलाफ 2019 के जवाबी हवाई हमला आ ऑपरेशन सनराइज – भारत-म्यांमार के संयुक्त आतंकवाद विरोधी हमला। सितंबर 2019 में चौहान के पूर्वी कमान के जनरल ऑफिसर कमांडिंग इन चीफ (जीओसी-इन-सी) के रूप में नियुक्त कइल गइल रहे। मई 2021 में रिटायरमेंट ले ऊ एह पद पर रहलें <ref>{{Cite web|url=https://www.aajtak.in/india/news/story/biography-of-new-cds-lieutenant-general-anil-chauhan-balakot-airstrike-pakistan-china-tstrd-1546556-2022-09-29|title=बालाकोट एयरस्ट्राइक के वक्त सेना के DGMO थे अनिल चौहान, चीन मामलों के एक्सपर्ट... अब बने देश के नए CDS|date=2022-09-29|website=आज तक|language=hi|access-date=2022-10-04}}</ref> ।
== संदर्भ ==
2bprt3zej1sdrq7qpdf8b2laugc0nau
गढ़वाल राइफल
0
89506
803350
766313
2026-07-29T16:22:46Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803350
wikitext
text/x-wiki
{{cleanup translation}}
{{Infobox military unit|unit_name=गढ़वाल राइफल्स एंड गढ़वाल स्काउट्स|image=Gharwal scouts and Gharwal rifles.jpg|image_size=|caption=गढ़वाल राइफल्स एंड गढ़वाल स्काउट्स का सैन्य चिह्न|dates=1887 – वर्तमान|country={{flag|भारत}}|branch=[[File:Flag of Indian Army.svg|24px]] [[भारतीय थलसेना]]|type=पैदल सेना|size=22 बटालन|command_structure=|garrison=[[लैंसडाउन]], [[गढ़वाल मण्डल|गढ़वाल]], [[उत्तराखण्ड]]|garrison_label=रेजिमेंटल सेंटर|equipment=|equipment_label=|nickname=स्नो लेपर्ड
The Royal Garhwalis
The Veer Garhwalis
Gallant Bhullas|march=बढ़े चलो गढ़वालियों|colonel_of_the_regiment=लेफ्टिनेंट जनरल एन॰एस॰ राजा सुब्रमणि<ref>{{Cite web|url=http://www.newindianexpress.com/cities/thiruvananthapuram/2018/may/07/lt-gen-cherish-mathson-appointed-colonel-of-regiment-of-garhwal-rifles-1811124.html|title=Lt Gen Cherish Mathson appointed Colonel of Regiment of Garhwal Rifles}}</ref>|motto=''युधाया कृत निश्चय'' ''(दृढ़ संकल्प के साथ लड़ो)''|colors=''बद्री विशाल लाल की जय'' ''(भगवान बद्री नाथ के पुत्रों की विजय)''|colors_label=सिंहनाद|anniversaries=5 मई 1887|decorations=3 विक्टोरिया क्रॉस, 1 [[अशोक चक्र]], 4 [[महावीर चक्र]], 14 [[कीर्ति चक्र]], 52 [[वीर चक्र]], 44 [[शौर्य चक्र]]|battle_honours='''स्वतंत्रता के बाद'''
टिथवाल, नूरनंग, गदरा रोड, बटुर डुग्रांडी, हिल्ली, बटालिक, ड्रास
<!-- Insignia -->|identification_symbol=अशोक प्रतीक के साथ एक माल्टीज़ क्रॉस|identification_symbol_label=बिल्ला|identification_symbol_4_label=रेजिमेंटल ध्वज}}
'''गढ़वाल राइफल्स''' भारतीय सेना के एगो सेना रेजिमेंट ह। एकरा के मूल रूप से 1887 में बंगाल सेना के '''39वीं (गढ़वाल) रेजिमेंट''' के रूप में उठावल गइल रहे। एकरा बाद ई ब्रिटिश भारतीय सेना के हिस्सा बन गइल आ भारत के आजादी के बाद ई भारतीय सेना में समा गइल। <ref name=":0">{{Cite web|url=http://www.indianphilately.net/news1116.html|title=Indian Philately Digest : News : November 2016|website=www.indianphilately.net|access-date=2022-04-20}}</ref>
ई 19वीं सदी के अंत आ 20वीं सदी के सुरुआत के सीमा अभियान सभ में भी काम कइलस आ साथ ही साथ बिस्व जुद्ध सभ आ आजादी के बाद लड़ल गइल लड़ाई सभ में भी काम कइलस। <ref name=":0" /> ई मुख्य रूप से राजपूत आ ब्राह्मण गढ़वाली के सैनिकन से बनल बा जे मुख्य रूप से गढ़वाल क्षेत्र के सात गो जिला (चमोली, रुद्रप्रयाग, तहरी गढ़वाल, उत्तरकाशी, देहरादून, पौड़ी गढ़वाल, हरिद्वार) से आवेलें। <ref name=":1">{{Cite web|url=https://www.globalsecurity.org/military/world/india/rgt-garhwal.htm|title=Garhwal Rifles|website=www.globalsecurity.org|access-date=2022-04-20}}</ref> आज ई 25,000 से ढेर सैनिकन से बनल बा, जे एकइस गो नियमित बटालियन (अर्थात 2 से 22वीं) में संगठित बाड़ें, गढ़वाल स्काउट जे स्थायी रूप से जोशिमाथ पर तैनात बाड़ें आ 121 इन्फ बीएन टीए आ 127 इन्फ बीएन टीए (इको) आ 14 आरआर दू बटालियन के... 36 आरआर, 48 आरआर बटालियन समेत टेरिटोरियल आर्मी भी ए रेजिमेंट के हिस्सा बा। <ref name=":1" /> तब से 1st बटालियन के मशीनी इन्फैंट्री में बदल दिहल गइल बा आ एकर 6th बटालियन के रूप में मशीनी इन्फैंट्री रेजिमेंट के हिस्सा बा। <ref name=":0" />
रेजिमेंट के निशान में माल्टीज क्रॉस शामिल बा आ ई निष्क्रिय राइफल ब्रिगेड (प्रिंस कंसोर्ट के आपन) पर आधारित बा काहें से कि ई एगो निर्धारित राइफल रेजिमेंट हवे। नियमित राइफल रेजिमेंट के उलट इ भारतीय सेना के समारोह में इस्तेमाल होखेवाला नियमित मार्चिंग रैंक में अयीसन 10 यूनिट में से एगो ह।
== आरंभिक इतिहास ==
1887 तक गढ़वाल लोग के बंगाल इन्फैंट्री अउरी पंजाब फ्रंटियर फोर्स के गोर्खा के पांच रेजिमेंट में शामिल कइल गइल| 1857 में दिल्ली के घेराबंदी में प्रसिद्धि हासिल करे वाली सिरमुर बटालियन (बाद में 2 गोरखा) के रोल में ओह घरी 33% गढ़वाले रहे। मूल रूप से गढ़वाली लोग के जवन उपलब्धि मिलल रहे <sup>ओकर श्रेय गोरखा लोग के दिहल जात रहे।</sup>
गढ़वाली के अलग रेजिमेंट बनावे के पहिला प्रस्ताव जनवरी 1886 में महामहिम लेफ्टिनेंट जनरल, (बाद में फील्ड मार्शल) सर एफएस रॉबर्ट्स, वीसी, भारत के तब के कमांडर इन चीफ द्वारा शुरू कइल गइल। एह हिसाब से अप्रैल 1887 में 3 (द कुमाऊन) गोरखा रेजिमेंट के 2 बटालियन के ऊपर उठावे के आदेश दिहल गइल, जवना में गढ़वाली अउरी कुमाऊनी आदमी के छह गो मिश्रित कंपनी अउरी दू गो गोरखा के चौकोर संरचना रहे| एह फैसला के आधार पर अपर गढ़वाल आ तिहरी राज्य के इलाका में मेजर एल कैम्पबेल आ कैप्टन ब्राउन के ओर से बहाली शुरू कइल गइल| बटालियन के चउथा गोरखा के लेफ्टिनेंट कर्नल ईपी मैनवारिंग उठावले रहले। मेजर एलआरडी कैम्पबेल दूसरा कमान आ लेफ्टिनेंट कर्नल जेएचटी इवाट, एडजुटेंट, दुनो पंजाब फ्रंटियर फोर्स के रहले। लेफ्टिनेंट कर्नल ई.पी.
1891 में दुनो गोर्खा कंपनी आगे चल के 2 बटालियन 3 गोरखा राइफल्स के नाभिक बन गइल अउरी बाकी बटालियन के फिर से बंगाल इन्फैंट्री के 39वीं (गढ़वाली) रेजिमेंट के रूप में नामित कइल गइल| गोरखा लोग के ‘क्रॉस खुकरी’ के जगह ‘फीनिक्स’ ले लिहलस, जवन पौराणिक चिरई शिखा में अपना राख से उठत रहे, जवना से गढ़वाली के औपचारिक शुरुआत एगो अलग वर्ग रेजिमेंट के रूप में भइल। 1892 में 'राइफल्स' के आधिकारिक उपाधि मिल गइल। बाद में ‘फीनिक्स’ के गिरा दिहल गइल, आ माल्टीज क्रॉस जवन राइफल ब्रिगेड (प्रिंस कंसोर्ट के आपन) के इस्तेमाल में रहे, ओकरा के अपनावल गइल।
रेजिमेंटल सेंटर के स्थापना 1 अक्टूबर 1921 के लैंसडाउन में भइल।
== पहिला विश्वयुद्ध (1914-18) में भइल। ==
[[चित्र:Stamp_of_India_-_1987_-_Colnect_164954_-_The_Garhwal_Rifles_and_Garhwal_Scouts.jpeg|right|thumb|305x305px| 1987 के डाक टिकट लगावल गइल]]
महायुद्ध में फ्रांस के गढ़वाली लोग जे मेरठ डिवीजन के गढ़वाल ब्रिगेड के हिस्सा रहल, फ्लैंडर्स में कार्रवाई में डूब गइल जहाँ दुनों बटालियन बहादुरी से लड़ल। रेजिमेंट के दू गो विक्टोरिया क्रॉस जीते के गौरव रहे; फेस्टुबर्ट में एन के दरवान सिंह नेगी आ न्यूवे चैपल में आरएफएन गबर सिंह नेगी (मृत्यु के बाद)। एनके दरवान सिंह के ई गौरव भी रहल कि ऊ पहिला भारतीय रहलें जिनके राजा सम्राट द्वारा व्यक्तिगत रूप से विक्टोरिया क्रॉस से सम्मानित कइल गइल, सभसे खास रहल फ्रांस में 1 दिसंबर 1914 के लोकोन में भइल लड़ाई के फ्रंट लाइन पर। चूंकि जानमाल के नुकसान बहुत ढेर रहे एह से बटालियन सभ के अस्थायी रूप से मिला के "द गढ़वाल राइफल्स" नाँव दिहल गइल (दुनो गढ़वाल बटालियन सभ के 14 गो अफसर, 15 गो वीसीओ आ 405 लोग फ्रांस में मारल गइल)। फ्रांस में इंडियन कोर के कमान संभाले वाला लेफ्टिनेंट जनरल सर जेम्स विल्कॉक्स अपना किताब "विद द इंडियन इन फ्रांस" में गढ़वाली लोग के बारे में इ बात कहले रहले कि "1 अउरी 2 दुनो बटालियन हर मौका प शानदार प्रदर्शन कइलस, जवना में उ लोग लागल रहे।" ... गढ़वाली लोग अचानक हमनी के सबसे बढ़िया योद्धा के रूप में आगे उछल गइल ... ओ लोग के उत्साह अउरी अनुशासन से बढ़िया कुछूओ ना हो सकत रहे"।
[[null|right|link=https://en.wikipedia.org/wiki/File:The_Garhwali_Soldier.jpg|thumb| अदम्य गढ़वाली सैनिक : युद्ध स्मारक, लैंसडाउन]]
बाद में 1917 में पुनर्गठित 1 आ 2 बटालियन में मेसोपोटामिया में तुर्कन के खिलाफ कार्रवाई भइल। खान बगदादी में 25–26 मार्च 1918 के लेफ्टिनेंट कर्नल हॉग के कमान में 2 बटालियन खुद के एगो तुर्की कॉलम (जवना में 300 सभ रैंक से बनल रहे, जेह में उनके डिवीजनल कमांडर आ स्टाफ भी शामिल रहल) द्वारा घेरल आ आत्मसमर्पण करे के मजबूर हो गइल। लेफ्टिनेंट कर्नल नम्ब, एमसी के कमान में 1 बटालियन 29-30 अक्टूबर 1918 के शर्कत में तुर्कन के खिलाफ कार्रवाई में आपन अलग पहचान बनवलस। बटालियन के बहादुरी के पुरस्कार मिलल. महान युद्ध, बैटल ऑनर्स "ला बस्सी", "आर्मेंटिएरेस", "फेस्टबर्ट", "न्यूवे चैपल", "ऑबर्स", "फ्रांस एंड फ्लैंडर्स 1914-15", "मिस्र", "में उनके युद्ध कौशल की उचित मान्यता के रूप में. मैसिडोनिया", "खान बगदादी", "शर्कत", "मेसोपोटामिया" आ "अफगानिस्तान" के पुरस्कार एह रेजिमेंट के दिहल गइल. 3 बटालियन के 1916 में आ 4वीं के 1918 में उठावल गइल; एह दुनो बटालियन के अफगानिस्तान अउरी उत्तर-पश्चिमी सीमा में कार्रवाई भईल| (एही बीच 1917 में मौजूदा तीन गो गढ़वाली बटालियन सभ से ड्राफ्ट के माध्यम से 4वीं बटालियन के गठन भइल, जेह में ज्यादातर कुमाऊं आदमी रहलें; एकर नाँव बदल के 4वीं बटालियन 39वीं कुमाऊँ राइफल्स, आ फिर 1918 में 1st बटालियन 50वीं कुमाऊं राइफल्स रखल गइल) .
अक्टूबर 1919 में वजीरी आ मशूद के खिलाफ कार्रवाई खातिर 4 बटालियन के कोहाट भेजल गइल| कोहट में ऑपरेशन के सफलतापूर्वक पूरा कइला के बाद बटालियन के कोटकाई के लगे स्पिन घर रिज प बहुत महत्वपूर्ण, लेकिन कठिन घेराबंदी प कब्जा करे के काम दिहल गइल। एकरे परिणामस्वरूप 2 जनवरी 1920 के मशुडो लोग के हमला में कंपनी के कमांडर लेफ्टिनेंट डब्ल्यूडी केनी भारी गोलीबारी अउरी कट्टरपंथी आदिवासी लोग के लहर के नीचे आपन जगह बना लेले| कंपनी के भारी नुकसान भईल बा। आखिरकार जब पिकेट के वापसी के आदेश मिलल त पार्टी प लगातार घात लगा के हमला कइल गइल, जवना के चलते अउरी जानमाल के नुकसान भईल। लेफ्टिनेंट केनी भले ही बहुत घायल हो गइल रहले लेकिन आदिवासी लोग के साहसिक लड़ाई देके अपना आदमी के बाहर निकाले में मदद कइले, जब तक कि अंत में उ गिर गइले अउरी अपना चोट के चलते उनुकर मौत ना हो गइले। भारी संख्या में महसुद के खिलाफ उनुका उभरत बहादुरी खातिर लेफ्टिनेंट डब्ल्यूडी केनी के मरणोपरांत तीसरा विक्टोरिया क्रॉस से सम्मानित कइल गइल। मशहूर स्पिन घर रिज के नाम बदल दिहल गइल आ बाद में एकरा के ‘गढ़वाली रिज’ के नाम से याद कइल गइल।
== रॉयल का पुनर्गठन और शीर्षक ==
[[चित्र:Qissakhwanibazaarmassacre.jpg|right|thumb| किस्सा ख्वानी बाजार नरसंहार में गढ़वाल राइफल्स ड्यूटी से इनकार क देले रहे।]]
1 अक्टूबर 1921 के भारतीय सेना के पुनर्गठन के हिस्सा के रूप में भारतीय सेना में ‘ग्रुप’ प्रणाली शुरू भईल अउरी रेजिमेंट 18वां भारतीय पैदल सेना समूह बन गइल| एही दिन लेफ्टिनेंट कर्नल केनेथ हेंडरसन, डीएसओ के नेतृत्व में चउथा बटालियन के समूह के प्रशिक्षण बटालियन के रूप में नामित कइल गइल| एही साल 1 दिसंबर के एकर नाम बदल के 10/18वीं रॉयल गढ़वाल राइफल्स रखल गइल। आज भी दिग्गज लोग बोलचाल में एह रेजिमेंट के गढ़वाल ग्रुप के नाम से संबोधित करेला।
2 फरवरी 1921 के दिल्ली में अखिल भारतीय युद्ध स्मारक (अब ‘इंडिया गेट’ कहल जाला) के शिलान्यास के ऐतिहासिक अवसर पर कनाट के ड्यूक घोषणा कइले कि विशिष्ट सेवा आ बहादुरी के सम्मान में सम्राट छह गो पुरस्कार दिहले बाड़न यूनिट आ दू गो रेजिमेंट के ‘रॉयल’ के उपाधि से सम्मानित कइल गइल जवना में से एगो रेजिमेंट रहे. ‘रॉयल’ गढ़वाल राइफल्स के दाहिना कंधा प लाल रंग के मुड़ल लाल रस्सी (रॉयल रोप) अउरी कंधा के टाइटिल प ट्यूडर मुकुट पहिनला के विशेष निशान दिहल गइल। ‘शाही’ के उपाधि आ ताज 26 जनवरी 1950 के गिरावल गइल, जब भारत गणतंत्र बनल, हालांकि डोरी आपन भेद बरकरार रखले बा।
== द्वितीय विश्वयुद्ध (1939-45) में भइल। ==
दुसरा बिस्व जुद्ध के सुरुआत के बाद एह रेजिमेंट के बिस्तार भइल आ 1940 में 4वीं बटालियन के फिर से उठावल गइल; 5वीं के स्थापना 1941 में भइल रहे। 11वीं (टेरिटोरियल) बटालियन के 1939 में पेशावर में संचार सुरक्षा के काम खातिर पलल बढ़ल; 1941 में एकरा से 6वीं बटालियन के गठन भईल|
दुसरा बिस्व जुद्ध में गढ़वाली लोग के सक्रिय भागीदारी भइल, बर्मा में 1 आ 4 बटालियन, मलय में 2 आ 5 बटालियन के कार्रवाई देखल गइल। दूसरा बटालियन 1940 में मलय प्रायद्वीप के कुआंतानी में गैरीसन बटालियन रहे| कुआंटन में सेना के एकमात्र बटालियन, एकरा के एगो व्यापक रूप से बिखराइल इलाका पर असंख्य काम खातिर मैदान में उतारल गइल रहे। जापानी आक्रमण से ठीक पहिले एकरा के दू बेर दुहावल गइल ताकि नया बटालियन बनावे में मदद मिल सके। जब जापानी हमला कइले त बटालियन बहादुरी से लड़लस, जवना में भारी नुकसान भईल। बटालियन के बैटल ऑनर ‘कुआंटन’ आ थिएटर ऑनर ‘मलया 1941-42’ से सम्मानित कइल गइल. भारी जानमाल के नुकसान के कारण मलय अभियान के बाद 2 बटालियन के अस्तित्व खतम हो गइल – अवशेष जापानी लोग द्वारा पकड़ लिहल गइल। नवगठित 5वीं बटालियन के दिसंबर 1941 में विदेश में आदेश दिहल गइल रहे, जबकि अभी भी कच्चा अउरी कम सुसज्जित रहे| ई मध्य पूर्व खातिर रवाना हो गइल, हालाँकि सिंगापुर गइला के बाद गंतव्य बदल दिहल गइल। बटालियन मूर, जोहोर में कुछ उल्लेखनीय कार्रवाई आ फिर सिंगापुर जाए के रास्ता में लंबा, कड़वा रियरगार्ड कार्रवाई लड़लस। 7वीं बटालियन के अनिवार्य रूप से एह दुनो बटालियन के जगह के रूप में उठावल गइल (बाद में एकरा के प्रशिक्षण भूमिका में बदल दिहल गइल)। दूसरा बटालियन के 1946 में युद्ध के बाद ही फिर से उठावल गइल| 5वीं के फेर से खड़ा करे खातिर 1962 तक इंतजार करे के पड़ी|
1941 में 1 बटालियन बर्मा गइल अउरी जापानी ज्वार के रोके के कोशिश में बहादुरी से लड़ाई कइलस| ई दक्षिणी शान राज्यन के येनंगयुंग में भइल हताश लड़ाई में भाग लिहलस, जेकरा खातिर एकरा के लड़ाई के सम्मान के रूप में सम्मानित कइल गइल। एकरे अलावा एकरा के बैटल ऑनर "मोनवा" के एकलौता पुरस्कृत होखे के गौरव बा, ई रिट्रीट फ्रॉम बर्मा में आखिरी बड़हन एक्शन रहल। कुछ समय के आराम अउरी जंगल के गहन प्रशिक्षण के बाद फिर से समूहबद्ध होखे के बाद बटालियन के फिर से समूह बनावे खातिर बर्मा वापस कर दिहल गइल| अरकान में एकर कार्रवाई, न्गाक्यादौक दर्रा, रामरी में उतरल आ रंगून में अंतिम प्रवेश से एकरा के अउरी लड़ाई के सम्मान मिलल: "उत्तर अरकान", "ंगाक्यडुक दर्रा", "रामरी" आ "तुंगुप", आ थिएटर ऑनर "बर्मा 1942-". 45" के बा।
NW फ्रंटियर पर लगभग तीन साल के बाद 4 बटालियन के भी बर्मा के आदेश दिहल गइल| जंगली युद्ध के गहन प्रशिक्षण के बाद ई बर्मा चल गइल आ सुरंग के मैदान अक्याब आ फिर कुआलालंपुर में जाए से पहिले कुआलालंपुर में आत्मसमर्पण करे वाला जापानी लोग के निहत्था करे खातिर कई गो कार्रवाई लड़लस।
उत्तरी अफ्रीका आ इटली के नाम से जानल जाला। 3 बटालियन एबिसिनिया, पश्चिमी रेगिस्तान, मिस्र, साइप्रस, इराक, सीरिया, फिलिस्तीन आ अंत में इटली में अभियान में काम कइलस। एबिसिनिया में दुसरा बिस्व जुद्ध के सुरुआती दौर में ई इटैलियन लोग के खिलाफ आपन छाप छोड़लस आ पूरा अभियान में खुद के अलग कइलस। एकरा युद्ध सम्मान में तीन गो 'गढ़वाली-केवल' सम्मान बा: "गल्लाबत", "बरेंटू" आ "मसावा"। एकरे बाद अउरी लड़ाई के सम्मान मिलल: "केरेन", "अम्बा अल्गी", "सिटा डी कैस्टेलो", आ थिएटर के सम्मान "उत्तर अफिरका 1940-43" आ "इटली 1943-45" के, बिबिध लड़ाई के मैदान सभ पर मजबूत शौर्य के गवाही आ... थियेटरन में बा।दे रहल बा।
युद्ध के अंत अउरी एकरे परिणामस्वरूप डिमोबिलाइजेशन के बाद रेजिमेंट के लगे तीन गो नियमित बटालियन 1, 2 अउरी 3 हो गइल| एह तरह से आजादी के समय गढ़वाल राइफल्स में मात्र तीन गो सक्रिय बटालियन रहे|
== आजादी के बाद ==
1947 में भारत के गठन आ ओह समय भारत में बिबिध राज्य सभ के विलय के बाद रियासत गढ़वाल पहिला रियासत रहल जे भारतीय संघ में विलय भइल। एकरा बाद रेजिमेंट के तबादला नव स्वतंत्र भारतीय सेना में कर दिहल गइल| 3 बटालियन जम्मू कश्मीर ऑपरेशन में विशिष्टता के साथ भाग लिहलस, बैटल ऑनर "तिथवाल" जीतलस आ आजादी के बाद से कवनो एक ऑपरेशन में भारतीय सेना के सबसे सजावल बटालियन में से एगो बने के गौरवशाली मिलल - एकरा के एमवीसी, 18 वोन जीतल वीआरसी, 01 एससी के बा। (तब अशोक चक्र वर्ग III कहल जाला) आ 19 गो मेन्शन-इन-डिस्पैच। कमांडिंग ऑफिसर लेफ्टिनेंट कर्नल कमन सिंह के एगो हकदार महावीर चक्र से सम्मानित कइल गइल आ उनकर नाम ‘कमन सेतु’ के रूप में जिंदा बा, जवन कि पोक आ जम्मू-कश्मीर के बीच हाल ही में खुलल क्रॉसिंग प्वाइंट (अब ‘अमन कामन सेतु’ के नाम बदलल गइल बा) के रूप में जिंदा बा।
1950 में जब भारत गणतंत्र बनल त रेजिमेंट के नाम से शाही उपाधि हटा दिहल गइल। अंग्रेजन से जुड़ल अउरी रेजिमेंटल निशान भी बंद कर दिहल गइल, हालांकि रेजिमेंट के डोरी के दाहिना कंधा प पारंपरिक ‘शाही’ फैशन में पहिरल जारी रहल| 1953 में रेजिमेंट के 3 बटालियन कोरिया में संयुक्त राष्ट्र के गार्जियन फोर्स में योगदान दिहलस|
== 1962 के चीन-भारत के युद्ध ==
1962 के चीन-भारत संघर्ष में नेफा के तवांग, जंग आ नुरानांग सेक्टर में भारी लड़ाई के बीच 4 बटालियन (ओह समय के रेजिमेंट के सबसे कम उमिर के बटालियन) के देखल गइल, जहाँ ऊ अपना बारे में बेहतरीन ब्यौरा दिहलस, बहुत नुकसान उठावलस भारी जानमाल के नुकसान भइल. , 1999 में भइल रहे। नुरानांग में बटालियन के स्टैंड के एह लड़ाई के अधिकतर वर्णन में "सेना के लड़ाई के सभसे बढ़िया उदाहरण" के रूप में एकल कइल गइल बा। भारी विषमता के खिलाफ बहादुरी से खड़ा होखे खातिर 4 गढ़ आरआईएफ के बैटल ऑनर "नुरनंग" से सम्मानित कइल गइल – एकलौता बटालियन जेकरा के नेफा में बैटल ऑनर से सम्मानित कइल गइल, जवन कि ओह खास संघर्ष के संदर्भ में एगो एकल भेद रहे। नूरानांग के नाम बदल के जसवंतगढ़ रखल गइल आरएफएन जसवंत सिंह रावती के सम्मान में, जिनकर नूरानांग में बहादुरी के मरणोपरांत महा वीर चक्र से सम्मानित कइल गइल। एह संघर्ष में जीतल दूसरा महा वीर चक्र लेफ्टिनेंट कर्नल (बाद में मेजर जनरल) बीएम भट्टाचार्य के जीतल रहे, जे अथक कमांडिंग ऑफिसर रहले, जेकरा नेतृत्व में चउथा बटालियन चीनी के खूनी नाक दे दिहलस। कैद में बटालियन के बचे वाला लोग के चीनी POW कैंप में अतिरिक्त सजा खातिर चुनल गइल काहे कि चीनी लोग के गढ़वाली लोग के हाथ से भारी नुकसान भइल रहे। नुरानांग में बटालियन के पौराणिक कार्रवाई लोककथा में आ गइल बा. बटालियन के मिलल बहादुरी के पुरस्कार दू गो महा वीर चक्र, सात गो वीर चक्र, एगो सेना मेडल आ एगो मेन्शन इन डेस्पेच रहल.
== 1965 के भारत-पाकिस्तान युद्ध ==
1965 में राजस्थान के बार्मर सेक्टर में पहिला बटालियन शानदार कार्रवाई कइलस, जवना खातिर बाद में एकरा के बैटल ऑनर "गदरा रोड", ओपी हिल में 2 बटालियन, फिलोरा में 6 बटालियन अउरी बुतुर डोग्रांडी में 8 बटालियन से सम्मानित कइल गइल। दू दिन का भीतर ओकरा दू गो वरिष्ठ अधिकारी लेफ्टिनेंट कर्नल जेई झीराद आ मेजर एआर खान के नुकसान हो गइल. एकरा बाद कमान सबसे छोट कैप्टन (लेफ्टिनेंट कर्नल) एचएस रौटेला के हाथ में चल गइल जवन मोर्चा से नेतृत्व कइले अउरी बहादुरी खातिर तब के चीफ ऑफ आर्मी स्टाफ द्वारा तारीफ कइल गइल| रेजिमेंट के कैप्टन सीएन सिंह के मरणोपरांत 120 इन्फैंट्री ब्रिगेड मुख्यालय में सेवा में रहत बहादुरी खातिर एमवीसी से सम्मानित कइल गइल। पहिला बटालियन आ आठवीं बटालियन के क्रमशः बैटल ऑनर्स ‘गदरा रोड’ आ ‘बुट्टूर डोगरांडी’ से सम्मानित कइल गइल. छठवीं बटालियन आ 8वीं बटालियन के रंगमंच सम्मान ‘पंजाब 1965’ से भी सम्मानित कइल गइल।
गदरा शहर के लड़ाई के दौरान पहिला बटालियन राजस्थान सेक्टर में रहे आ गदरा शहर पर कब्जा करे के अभियान में आपन अलगा पहचान बनवलस, बिना तोपखाना के समर्थन के रेगिस्तानी इलाका में सेना के रणनीति के बढ़िया प्रदर्शन कइलस। बटालियन जेसी के पार, नवा ताला आ मियाजलर पर कब्जा कर लिहलसि. वीर चक्र से सम्मानित लोग में सीओ लेफ्टिनेंट कर्नल केपी लाहिरी भी शामिल रहे। बटालियन के बैटल ऑनर ‘गदरा रोड’ आ थिएटर ऑनर ‘राजस्थान 1965’ मिलल. कप्तान नरसिंह बहादुर सिंह जी के अहम भूमिका रहल आ उनुका बहादुरी आ साहसी प्रयास खातिर उनुका के बटालियन के मिलल ‘सेना मेडल’ बहादुरी के पुरस्कार, थारी वीर चक्र आ पांच गो मेन्शन इन डेस्पैच से सम्मानित कइल गइल.
ऑपरेशन हिल के दौरान 2 बटालियन 'ओपी हिल' प दुगो हमला में भाग लेलस| 2 बटालियन के कैप्टन चंद्र नारायण सिंह मुख्यालय 120 इन्फैंट्री ब्रिगेड से जुड़े रहे। गलुठी इलाका में हमलावरन का खिलाफ रात में भइल बहादुरी से भइल कार्रवाई में ऊ ओह चार्ज के नेतृत्व कइलन जवना में दुश्मनन के छह गो मारल गइल जबकि बाकी लोग भारी मात्रा में हथियार, गोला बारूद आ उपकरण छोड़ के भाग गइल. एह कार्रवाई में कैप्टन सीएन सिंह के मशीनगन फटला से मारल गइल आ उनुकर जान चल गइल. उनुका के मरणोपरांत महा वीर चक्र से सम्मानित कइल गइल.
3 बटालियन लाहौर सेक्टर में रहे, आ जीटी रोड पर अग्रिम में भाग लिहलस। एकरा में 33 लोग के जानमाल के नुकसान भइल जवना के अधिकतर कारण दुश्मन के तोपखाना के बहुते भारी गोलाबारी भइल. 6वीं बटालियन सियालकोट में रहे जहाँ युद्ध के कुछ सबसे भयंकर लड़ाई भईल| शुरुआती चरण में बटालियन चरवा पर कब्जा कर लिहलस। एकरे बाद ई फिलाउरा के दृढ़ता से पकड़ लिहलस, दुश्मन के कई गो हमला के पीछे छोड़ दिहलस। 8वीं बटालियन भी सियालकोट सेक्टर में रहे, आ बतुर डोग्रांडी खातिर कड़ा लड़ाई लड़लस, जवना में भारी लड़ाई के दू दिन के भीतर कमांडिंग ऑफिसर आ 2आईसी के नुकसान समेत भारी कीमत चुकावे के पड़ल। एह कार्रवाई सभ से बैटल ऑनर "बतुर डोगरंडी" आ थिएटर ऑनर "पंजाब 1965" के माध्यम से रेजिमेंट के अउरी महिमा आइल। बटालियन के मिलल बहादुरी के पुरस्कार वीर चक्र रहे।
[[चित्र:A_Garhwal_Rifles_recruit_in_the_1970s.jpg|right|thumb| 1970 के दशक में गढ़वाल राइफल्स भर्ती।]]
== 1971 के युद्ध में भइल ==
बांग्लादेश के मुक्ति के अभियान के दौरान 5वीं बटालियन एगो यशस्वी रास्ता देखवलस| युद्ध में अपना हरकत खातिर बटालियन के बैटल ऑनर 'हिली' आ थिएटर ऑनर 'ईस्ट पाकिस्तान 1971' से सम्मानित कइल गइल। बटालियन के तीन गो वीर चक्र, तीन गो सेना मेडल आ सात गो मेन्शन इन डेस्पैच मिलल. 12वीं बटालियन अक्टूबर 1971 से एक्शन में रहे अउरी सक्रिय दुश्मनी शुरू होखला प दिनाजपुर के पूरब में सीमांकन अउरी ऑपरेशन में भाग लेलस|
3 बटालियन शकरगढ़ सेक्टर में रहे। ई आपन सुरुआती उद्देश्य धनदार आ मुखवाल (सुचेतगढ़ के दक्खिन) ले गइल आ फिर दुश्मन के इलाका में बारी आ लैसर कलां ले गइल। युद्धविराम के समय तक बटालियन चक्र के उत्तर में रामरी तक आगे बढ़ गइल रहे| बटालियन के मिलल बहादुरी के पुरस्कार वीर चक्र आ सेना मेडल रहल. चौथा बटालियन झंगार सेक्टर में रहे अउरी आपन जमीन पकड़ के दुश्मन के ठिकाना प छापा मारत रहे| 6वीं बटालियन सियालकोट सेक्टर में रहे। नवनपिंड पर फिर से कब्जा कइला के बाद बटालियन दुश्मन के इलाका के गहिराई में रक्षात्मक लड़ाई लड़लस, जवना से अपना सेक्टर के सामने दुश्मन के ठिकाना प तीन गो मजबूत छापामारी के भगा दिहलस। बटालियन के मिलल बहादुरी के पुरस्कार एगो वीर चक्र आ दू गो मेन्शन इन डेस्पैच रहे. 7वीं बटालियन छंब सेक्टर, 1999 में रहे।
मेजर एचएस रौतेला एसएम (अब लेफ्टिनेंट कर्नल) के कमान में 8वीं बटालियन भी पंजाब में होल्डिंग रोल में रहे अउरी दुश्मन के चौकी घुरकी प कब्जा क लेलस अउरी सेना मेडल से सम्मानित कइल गइल| युद्धविराम तक जारी गोलाबारी के बावजूद उ ए चौकी प रहले। बटालियन के मिलल बहादुरी के पुरस्कार दू गो सेना मेडल रहल.
मेजर महाबीर नेगी के नेतृत्व में दसवीं बटालियन अखनूर-जौरियान सेक्टर में रायपुर क्रासिंग प कब्जा क के एगो उल्लेखनीय कार्रवाई कइलस। एक हमला में कमांडिंग ऑफिसर लेफ्टिनेंट कर्नल ओंकर सिंह निजी तौर प नेतृत्व कइले, गंभीर रूप से घाही हो गइले अउरी बाद में उनुकर मौत हो गइल।
== शांति सेना के ओर से ऑपरेशन पवन (1987-88)। ==
एह अभियान में गढ़वाल राइफल के कुछ बटालियन के 1987 में वर्तमान प्रधानमंत्री श्री राजीव गांधी जी भेजले रहले| भारत के शांति सेना द्वारा ऑपरेशन पवन के दौरान 5वीं बटालियन अउरी 11वीं बटालियन लिट्टे (लिबरेशन टाइगर्स ऑफ तमिल ईलम) के कमान के भगावे के काम प रहे| श्रीलंका के स्थानीय क्षेत्र में बा। एह काम में लिट्टे के हमला में घायल बटालियन के एगो सदस्य के बचावे खातिर 5वीं बटालियन के राइफलमैन कुलदीप सिंह भंडारी के 11वीं बटालियन में शिफ्ट कर दिहल गइल। 11वीं बटालियन के बाकी सदस्यन के जान बचा के अदम्य हिम्मत देखवले आ वीर चक्र से सम्मानित भइले.
[[चित्र:18_Garhwal_Riles_Point_4700.jpg|thumb|बिंदु 4700 पर बा। पर हमला से पहिले 18वीं बटालियन के जवान...]]
== कारगिल युद्ध ==
17वीं बटालियन बटालिक उपक्षेत्र में रहे आ ओकरा के राष्ट्रीय राजमार्ग के किनारे जुबर टॉप के नजारा देखे वाला रिजलाइन जुबर हाइट्स के एरिया बम्प आ कालापथर पर हमला करे के काम दिहल गइल रहे। चढ़ाई कठिन रहे आ कैप्टन जिंटू गोगोई के पलटन के छोड़ के सगरी कंपनी ‘दिन के उजाला’ में रहे। बहादुर ‘भुल्लोन’ दुश्मन के भारी गोलीबारी का सोझा कालापथर पर कब्जा कर लिहले, आ फेर दुश्मन के यूएमजी स्थापन से आमने सामने आ गइले. दुश्मन के पूरा हैरानी के बात रहे कि कैप्टन गोगोई तुरंत यूएमजी सेंगर प हमला कइले, जवना में हाथ से हाथ मिला के भईल लड़ाई में दुगो घुसपैठिया के मौत हो गइल, जवना में उ जानलेवा रूप से घायल हो गइले। अपना सुरक्षा के अनदेखी करे में बहादुरी खातिर कैप्टन जिंटू गोगोई के मरणोपरांत वीर चक्र से सम्मानित कइल गइल बाकिर दुख के बात बा कि कैप्टन विक्रम बत्रा का तरह जे कारगिल शहीद भी रहले, उनुकर नाम के कवनो खास जानकारी नइखे Is known. बटालियन अगिला दिनन में ताजा हमला कइलस आ बम्प आ कालापथार पर कब्जा कर लिहलस। एह से अउरी सफलता के रास्ता खुलल – बटालियन आगे बढ़ के मुंथो ढलो परिसर में एगो अउरी प्रमुख फीचर ले लिहलस, अंत में भारी बर्फबारी आ फीचर के LoC के नजदीकी के कारण तोपखाना समेत दुश्मन के कारगर फायरिंग के बावजूद बिंदु हासिल कइलस।5285 ले लिहलस। ऑपरेशन विजय में भइल कारनामा खातिर एह बटालियन के बैटल ऑनर ‘बटालिक’ से सम्मानित कइल गइल. बटालियन के मिलल बहादुरी के पुरस्कार वीर चक्र आ मेन्शन इन डेस्पैच रहे। एकरा अलावा एह यूनिट के बेहतरीन काम खातिर जीओसी-इन-सी नॉर्दर्न कमांड यूनिट एप्रिसिएशन से भी सम्मानित कइल गइल। अंत में भारी बर्फबारी आ सुविधा के एलओसी से नजदीक होखे के कारण तोपखाना समेत दुश्मन के कारगर गोलीबारी के बावजूद प्वाइंट 5285 पर कब्जा कर लिहल गइल। ऑपरेशन विजय में भइल कारनामा खातिर एह बटालियन के बैटल ऑनर ‘बटालिक’ से सम्मानित कइल गइल. बटालियन के मिलल बहादुरी के पुरस्कार वीर चक्र आ मेन्शन इन डेस्पैच रहे। एकरा अलावा एह यूनिट के बेहतरीन काम खातिर जीओसी-इन-सी नॉर्दर्न कमांड यूनिट एप्रिसिएशन से भी सम्मानित कइल गइल। अंत में भारी बर्फबारी आ सुविधा के एलओसी से नजदीक होखे के कारण तोपखाना समेत दुश्मन के कारगर गोलीबारी के बावजूद प्वाइंट 5285 पर कब्जा कर लिहल गइल। ऑपरेशन विजय में भइल कारनामा खातिर एह बटालियन के बैटल ऑनर ‘बटालिक’ से सम्मानित कइल गइल. बटालियन के मिलल बहादुरी के पुरस्कार वीर चक्र आ मेन्शन इन डेस्पैच रहे। एकरा अलावा एह यूनिट के बेहतरीन काम खातिर जीओसी-इन-सी नॉर्दर्न कमांड यूनिट एप्रिसिएशन से भी सम्मानित कइल गइल।
18वीं बटालियन के प्वाइंट 4700 अउरी ओकरा आसपास के ऊंचाई प कब्जा करे के काम दिहल गइल रहे जहाँ दुश्मन टोलोलिंग अउरी 5140 से बाहर निकालला के बाद आपन ठिकाना मजबूत कइले रहे| एकरा बाद के अभियान में भुल्ला उच्चतम स्तर के बहादुरी के प्रदर्शन कइले। कैप्टन सुमीत राय एगो साहसिक हमला के नेतृत्व कइलन, एगो सरासर ढलान पर चढ़ गइलन आ दुश्मन सेंगर के हैरान कर दिहलन. एह लोग के सफलता से पीटी 4700 पर कब्जा करे के राह खुल गइल. बाद में दुश्मन के गोलीबारी से ई बहादुर अधिकारी गंभीर रूप से घायल हो गइल आ अपना चोट का चलते ओकर मौत हो गइल. कप्तान सुमीत राय के मरणोपरांत वीर चक्र से सम्मानित कइल गइल. पास के फीचर प 18वीं के अवरू कंपनी हमला कइले। चार्ज के अगुवाई करत मेजर राजेश साह अउरी कैप्टन एमवी सूरज कई गो गढ़ से दुश्मन के भगा देले। एह दुनु वीर अफसरन के साहसी नेतृत्व आ बहादुरी खातिर वीर चक्र से सम्मानित कइल गइल. 18वीं बटालियन के भुल्लन अदम्य हिम्मत के प्रदर्शन कइलन, एनके कश्मीर सिंह, आरएफएन अनुसुया प्रसाद आ आरएफएन कुलदीप सिंह सभे मरणोपरांत वीर चक्र जीतले. बटालियन के सीओएएस यूनिट प्रशस्ति पत्र के तत्काल पुरस्कार मिलल जवना में छह गो वीर चक्र शामिल बा, एक बेर सेना मेडल, सात गो सेना मेडल, सात गो मेन्शन इन डेस्पैच आ बैटल ऑनर ‘ड्रास’ के पुरस्कार मिलल.
दुनु बटालियन के थिएटर ऑनर ‘कारगिल’ से सम्मानित कइल गइल. एह सम्मानन के बहुते कीमत चुकावे के पड़ल जवना में रेजिमेंट के हर रैंक के 49 गो जवानन के नुकसान भइल जवना के कार्रवाई में मारल गइल. कारगिल में 17वीं आ 18वीं बटालियन के अलावा रेजिमेंट के 10वीं बटालियन भी तैनात रहे। एह बटालियन के अनुकरणीय प्रदर्शन खातिर जीओसी-इन-सी नॉर्दर्न कमांड यूनिट एप्रिसिएशन आ चार गो सेना मेडल से सम्मानित कइल गइल.
[[चित्र:The_Garhwal_Rifles_marching_contingents_passes_through_the_Rajpath,_on_the_occasion_of_the_67th_Republic_Day_Parade_2016,_in_New_Delhi_on_January_26,_2016.jpg|alt=|right|thumb|305x305px| 67वीं गणतंत्र दिवस परेड, 2016 के अवसर पर राजपथ से गुजरते गढ़वाल राइफल्स टुकड़ी]]
== इकाइयों ==
निम्नलिखित इकाई सभ गढ़वाल राइफल्स के हिस्सा हवें:<ref name=":1"/>
* 1st बटालियन (6 मेक इन्फ में बदलल गइल)
* 2 बटालियन (विक्टोरिया क्रॉस प्लाटून, न्यूवे चैपल, शानदार सेकंड) के बा।
* तीसरी बटालियन (दांतवाल या तीसरी) के बा।
* 4वीं बटालियन (नुरनांग बटालियन) के बा।
* 5वीं बटालियन (हिल) के बा।
* 6वीं बटालियन के बा
* 7वीं बटालियन के बा
* 8वीं बटालियन के बा
* 9वीं बटालियन के बा
* दसवीं बटालियन के बा
* 11वीं बटालियन के बा
* 12वीं बटालियन के बा
* 13वीं बटालियन के बा
* 14वीं बटालियन के बा
* 15वीं बटालियन के बा
* 16वीं बटालियन के बा
* 17वीं बटालियन (बटालिक कारगिल) के बा।
* 18वीं बटालियन (ड्रास बटालियन) के बा।
* 19वीं बटालियन के बा
* 20वीं बटालियन के बा
* 21वीं बटालियन के बा
* 22वीं बटालियन के बा
* 121 पैदल सेना बटालियन क्षेत्रीय सेना (गढ़वाल) भारतीय सेना रिजर्व : कोलकाता, पश्चिम बंगाल
* 127 पैदल सेना बटालियन क्षेत्रीय सेना (गढ़वाल ईसीओ) भारतीय सेना रिजर्व : देहरादून, उत्तराखंड
* गढ़वाल स्काउट (स्काउट बटालियन) के बा।
* 14 राष्ट्रीय राइफल (गढ़ आरआईएफ) के बा।
* 36 राष्ट्रीय राइफल्स (गढ़ आरआईएफ) द गैलेंट या द गैलेंट 36
* 48 राष्ट्रीय राइफल (गढ़ आरआईएफ) के बा।
<ref name=":1"/>
== लड़ाई आ रंगमंच के सम्मान ==
आज तक रेजिमेंट के 33 बैटल ऑनर मिलल बा। एहमें से सात गो के आजादी का बाद के दौर में दिहल गइल बा. रेजिमेंट के निम्नलिखित रंगमंच के सम्मान भी मिलल बा: जम्मू-कश्मीर - 1947-48, पंजाब - 1965, राजस्थान - 1965, पूर्वी पाकिस्तान - 1971, कारगिल - 1999।
'''पहिला विश्वयुद्ध''' : "ला बासी 1914", "आर्मेन्टियर्स 1914", "फेस्टबर्ट 1914, 15", "न्यूव चैपल 1915", "ऑबर्स 1915", "फ्रांस आ फ्लैंडर्स 1914-15", "मिस्र 1915-16", " खान बगदादी 1918", "शर्कत 1918", "मेसोपोटामिया 1917-18", "मैसिडोनिया 1918"। 'अफगानिस्तान 1919' के बा।
'''द्वितीय विश्वयुद्ध''' : "गलाबत 1940", "बरेंटू 1941", "केरेन 1941", "मासावा 1941", "अम्बा अलागी 1941", 'अबिसिनिया 1940-41', 'उत्तर अफ्रीका 1940-43', "कुआंटन", ' मलय 1941-42', "येनांगयुंग 1942", "मोनवा 1942", "उत्तर अरकन 1944", "नागाकिदाउक दर्रा 1944", "रामारी 1945", "तौंगुप 1945", "बर्मा 1942-45", "सिट्टा डी कैस्टेलो 1944 " ", ", 'इटली 1943-45' के बा।
'''आजादी के बाद''' : "तिथवाल 1947-48", 'जम्मू कश्मीर 1947-48', "नूरंग 1962", "बतुर डोगरंडी 1965", 'पंजाब 1965', "गदरा रोड 1965", 'राजस्थान 1965', "हिली 1971 " ", 'पूर्वी पाकिस्तान 1971', 'ऑपरेशन पवन 1988', 'बटालिक 1999', 'द्रास 1999', 'कारगिल'।
== सजावट ==
रेजिमेंट के श्रेय तीन विक्टोरिया क्रॉस, एक एसी, चार एमवीसी, 15 केसी, 52 वीआरसी, 49 एससी, 16 पीवीएसएम, चार यूवाईएसएम, 13 वाईएसएम, 350 एसएम (नौ बेर समेत), 25 एवीएसएम (एक बेर समेत) अउरी 51 के बा वीएसएम (नौ बेर समेत) के बा।
'''सजावट (आजादी से पहिले) के बा।'''
विक्टोरिया क्रॉस के प्राप्तकर्ता लोग के बा:
* नायक दरवान सिंह नेगी - पहिला विश्वयुद्ध, फेस्टबर्ट - फ्रांस, 1914
* राइफलमैन गबर सिंह नेगी (मरणोत्तर) - पहिला विश्वयुद्ध, न्यूवे चैपल, 1915
* लेफ्टिनेंट विलियम डेविड केनी (मरण के बाद) - वजीरिस्तान अभियान, 1920
'''सजावट (आजादी के बाद) के बा।'''
अशोक चक्र के प्राप्तकर्ता लोग : १.
नाईक भवानी दत्त जोशी (मरणोपरांत), जून 1984, ऑपरेशन ब्लू स्टार, अमृतसर, भारत के सिख अलगाववादियन के खिलाफ अभियान के दौरान अपना हरकत खातिर
* लेफ्टिनेंट कर्नल कमन सिंह, 1947-1948 के भारत-पाकिस्तान युद्ध।
* लेफ्टिनेंट कर्नल बीएम भट्टाचार्य, चीन-भारत युद्ध, 1962
* राइफलमैन जसवंत सिंह रावत (मरण के बाद), चीन-भारत युद्ध, 1962
* कप्तान चंद्रनारायण सिंह, 1965 के भारत-पाकिस्तान युद्ध।
वीर चक्र के प्राप्तकर्ता लोग : १.
* राइफलमैन कुलदीप सिंह भंडारी द्वारा ऑपरेशन पवन, शांति सेना, 1988।
* कप्तान जिंटू गोगोई, ऑपरेशन विजय, 1999 के बा
* कर्नल (सेवानिवृत्त) तत्कालीन मेजर अजय कोठियाल 12 मई 2003 के कश्मीर के पुलवामा जिला में सर्जिकल स्ट्राइक के नेतृत्व कइले रहले।
* सूबेदार अजय वर्धन के ह
== रेजिमेंटल सेंटर - लैंसडाउन ==
लैंसडाउन, समुंद्र तल से 5,800 फीट (1,800 मीटर) के ऊँचाई पर, गढ़वाल राइफल्स के भर्ती केंद्र हवे। 1 अक्टूबर 1921 के रेजिमेंटल सेंटर आपन पहिला रेसिंग डे मनवलस। अब 1 अक्टूबर के बटालियन के रेसिंग डे के रूप में मनावल जाला। आजादी के बाद केंद्र के नाम बदल के गढ़वाल राइफल्स रेजिमेंटल सेंटर रखल गइल। प्रशिक्षण के दौरान कठोर अभ्यास हर रंगरूट में अनुशासन के भावना पैदा करे में मदद करेला। शारीरिक फिटनेस, मानसिक ताकत अउरी हथियार संभाले प खास जोर दिहल जाला। गढ़वाली के एगो युवक 34 सप्ताह के ट्रेनिंग कोर्स सफलतापूर्वक पूरा कइला के बाद फौजी में बदल जाला। एकरा बाद एह जवान के दू हफ्ता अउरी विद्रोह विरोधी अभियान के प्रशिक्षण दिहल जाला.
== रेजिमेंट के कर्नल ==
* ब्रिगेडियर जेटी इवाट, डीएसओ (03.03.1914-24.03.19) के बा।
* मेजर जनरल सर जेएचके स्टीवर्ट, केसीबी, डीएसओ (18.08.1939-30.11.1944) के बा।
* लेफ्टिनेंट जनरल सर आरबी डीड्स, केसीबी, ओबीई, एमसी (01.12.1944-31.05.1949) के ह।
* मेजर जनरल हीरा लाल अटल (01.06.1949-01.06.1959) के नाम से जानल जाला।
* मेजर जनरल जी भरत सेवाक सिंह, एमसी (05.07.1959-31.10.1970) के बा।
* मेजर जनरल एच एन सिंघल, पीवीएसएम, एवीएसएम (01.11.1970-31.12.1978) के बा।
* लेफ्टिनेंट जनरल के महेंद्र सिंह, पीवीएसएम (24.04.1979-06.04.1988) के बा।
* लेफ्टिनेंट जनरल आरवी कुलकर्णी, पीवीएसएम, यूवाईएसएम, एवीएसएम (07.04.1988-30.04.1993) के बा।
* मेजर जनरल एस सोंधी (01.05.1993-01.11.1994) के लिखल बा।
* मेजर जनरल एसपीएस कंवर, एवीएसएम, वीएसएम (02.11.1994-31.07.1996) के बा।
* ब्रिगेडियर एआईएस ढिलोन, वीएसएम (01.08.1996 - 31.07.1997) के बा।
* ब्रिगेडियर जगमोहन रावत (01.08.1997-30.09.2000) के बा।
* मेजर जनरल एडब्ल्यू रणभिसे, एवीएसएम, एसएम (01.10.2000-28.02.2003) के बा।
* लेफ्टिनेंट जनरल एमसी भंडारी, पीवीएसएम, एवीएसएम (01.03.2003-31.08.2006) के बा।
* लेफ्टिनेंट जनरल परमजीत सिंह, पीवीएसएम, एवीएसएम, वीएसएम (01.09.2006 - 31.05.2008) के बा।
* लेफ्टिनेंट जनरल बीके चेंगापा, पीवीएसएम, एवीएसएम (02.06.2008-31.12.2010) के बा।
* लेफ्टिनेंट जनरल सैयद अता हसनैन, पीवीएसएम, यूवाईएसएम, एवीएसएम, एसएम, वीएसएम** (01.01.2011-30.06.2013) के बा।
* लेफ्टिनेंट जनरल सारथ चंद, पीवीएसएम, यूवाईएसएम एवीएसएम, वीएसएम, एडीसी (01.07.2013-04.05.2018) के बा।
* लेफ्टिनेंट जनरल चेरिश मैथसन, पीवीएसएम, एसएम, वीएसएम (05.05.2018-04.08.2019) के बा।
* लेफ्टिनेंट जनरल एसके उपाध्याय पीवीएसएम, एसएम, एवीएसएम, वीएसएम के बा
* लेफ्टिनेंट जनरल एनएस राजा सुब्रमणि, एवीएसएम, एसएम के बा
== सब देखल जाव ==
[[श्रेणी:भारतीय सेना]]
[[श्रेणी:थल सेना]]
a1nuesxwysgu3sc440133qmjvq6n52l
नवीन एवं नवीकरणीय ऊर्जा मंत्रालय, भारत सरकार
0
90439
803332
768113
2026-07-29T16:21:30Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803332
wikitext
text/x-wiki
{{Infobox government agency
| name = नवीन एवं नवीकरणीय ऊर्जा मंत्रालय
| native_name = अक्षय ऊर्जा मंत्रालय
| native_name_a =
| native_name_r =
| type =
| seal = MNRE India.svg
| seal_size = 250px
| seal_caption =
| logo =
| logo_size =
| logo_caption =
| image = Raj Kumar Singh with PM Modi (cropped).jpg
| image_size =
| image_caption = [[राज कुमार सिंह]]
| formed = {{Start date and age|df=yes|1992}}
| preceding1 = Department of Non-conventional Energy Sources (DNES)
| preceding2 =
| dissolved =
| jurisdiction = [[भारत सरकार]]
| headquarters = [[नई दिल्ली]], [[भारत]]
| coordinates = <!--{{coord|LATITUDE|LONGITUDE|type:landmark_region:US|display=inline,title}}-->
| motto =
| employees =
| budget = {{INRConvert|5146.63|c}} <small>(2018-19 est.)<ref>{{cite web |url=http://www.indiabudget.gov.in/ub2018-19/eb/sbe95.pdf |title=Budget data |date=2019 |website=www.indiabudget.gov.in |access-date=15 September 2018 |archive-url=https://web.archive.org/web/20180304170727/http://www.indiabudget.gov.in/ub2018-19/eb/sbe95.pdf |archive-date=4 March 2018 |url-status=dead }}</ref></small>
| minister1_name = [[राज कुमार सिंह]]
| minister1_pfo = केंद्रीय मंत्री
| deputyminister1_name = भगवंत कुबा
| deputyminister1_pfo = राज्यमंत्री
| deputyminister2_name =
| deputyminister2_pfo =
| chief1_name = इंदु शेखर चतुर्वेदी भारतीय नौकरशाह
| chief1_position = (सचिव)
| chief2_name =
| chief2_position =
| chief3_name =
| chief3_position =
| chief4_name =
| chief4_position =
| chief5_name =
| chief5_position =
| chief6_name =
| chief6_position =
| chief7_name =
| chief7_position =
| chief8_name =
| chief8_position =
| chief9_name =
| chief9_position =
| parent_department =
| parent_agency =
| child1_agency =
| child2_agency =
| keydocument1 =
| website = https://mnre.gov.in/
| map =
| map_size =
| map_caption =
| footnotes =
}}
<references />
'''नया आ अक्षय ऊर्जा मंत्रालय''' ( '''MNRE''' ) [[भारत सरकार]] के एगो मंत्रालय हवे, जेकर नेतृत्व वर्तमान केंद्रीय कैबिनेट मंत्री [[राज कुमार सिंह]] करे लें, जे मुख्य रूप से अनुसंधान आ बिकास, बौद्धिक संपदा संरक्षण, आ अंतर्राष्ट्रीय सहयोग, संवर्धन, आ समन्वय के जिम्मेदारी लेले बा अक्षय ऊर्जा स्रोत जइसे कि पवन ऊर्जा, छोट हाइड्रो, बायोगैस, आ [[सौर ऊर्जा]] में .
मंत्रालय के व्यापक उद्देश्य [[भारत]] के ऊर्जा के जरूरत के पूरक खातिर नया अउरी अक्षय ऊर्जा के विकास अउरी तैनाती कइल बा . मंत्रालय के वर्तमान सचिव आनंद कुमार बाड़े .<ref>{{Cite web |title=Anand Kumar joins MNRE as Secretary |url=http://www.energynext.in/2017/07/anand-kumar-joins-mnre-as-secretary/ |access-date=2019-06-23 |website=Energynext |language=en-US |archive-date=2019-06-20 |archive-url=https://web.archive.org/web/20190620114054/http://www.energynext.in/2017/07/anand-kumar-joins-mnre-as-secretary/ |url-status=dead }}<cite class="citation web cs1" data-ve-ignore="true">[http://www.energynext.in/2017/07/anand-kumar-joins-mnre-as-secretary/ "Anand Kumar joins MNRE as Secretary"] {{Webarchive|url=https://web.archive.org/web/20190620114054/http://www.energynext.in/2017/07/anand-kumar-joins-mnre-as-secretary/ |date=2019-06-20 }}. </cite></ref>
मंत्रालय के मुख्यालय [[नई दिल्ली|नई दिल्ली के]] लोधी रोड में बा .<ref>[http://www.mnre.gov.in/contact.htm Contact Details] {{Webarchive|url=https://web.archive.org/web/20110820015739/http://www.mnre.gov.in/contact.htm|date=20 August 2011}}</ref> मंत्रालय के 2016-17 के वार्षिक रिपोर्ट के मुताबिक, भारत कई गो अक्षय ऊर्जा क्षेत्र में काफी प्रगति कइले बा जवना में शामिल बा, [[सौर ऊर्जा]], पवन ऊर्जा, अउरी जलविद्युत । <ref>{{Cite web |title=Annual Report |url=http://mnre.gov.in/file-manager/annual-report/2016-2017/EN/index.html |access-date=2018-09-15 |publisher=Mnre.gov.in}}<cite class="citation web cs1" data-ve-ignore="true">[http://mnre.gov.in/file-manager/annual-report/2016-2017/EN/index.html "Annual Report"]. </cite></ref>
==संदर्भ==
{{Reflist}}
qcirn4tkxhp5rpjw7edf5m210yxkm5d
बाबा अजगैबीनाथ धाम
0
92013
803354
802694
2026-07-29T16:23:04Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803354
wikitext
text/x-wiki
{{Copy edit|for=फ़ॉर्मेटिंग, हिज्जे आ टोन}}
'''अजबैगीनाथ मंदिर''' या '''अजगैवीनाथ धाम''' भारत के बिहार के [[भागलपुर जिला]] के सुल्तानगंज में स्थित हिन्दू लोग के एगो परसिद्ध मंदिर बा। हिन्दू लोग खातिर ई एगो महत्वपूर्ण तीर्थ स्थल हवे आ एकर धार्मिक महत्व बहुत बा। ई पवित्र [[गंगा नदी]] के किनारे बसल बा।{{Infobox Hindu temple|name=बाबा अजगैबीनाथ धाम|image=Ajgaibinath temple Day-View.png|caption=बाबा अजगैवीनाथ धाम|map_type=India Bihar|map_caption=Location in Bihar|coordinates={{coord|25|15|08.6|N|86|44|03.5|E|type:landmark_region:IN|display=inline,title}}|country=[[भारत]]|state=[[भारत]]|district=[[भागलपुर जिला]]|location=सुल्तानगंज, [[भागलपुर जिला]]|architecture=वैदिक-शैली|inscriptions=|creator=|temple_board=बाबा अजगैबीनाथ मंदिर समिति|website=}}{{हिंदू धर्म}}
== धार्मिक महत्व के बा ==
'''अजगैबीनाथ मंदिर''' के '''मनकमना मंदिर''' के नाम से भी जानल जाला। भक्त लोग के कहनाम बा कि ए जगह प कइल इच्छा पूरा हो जाला। एह संबंध में एह मंदिर के महंत प्रेमानंद जी के अनुसार '''''"एह जगह के अइसन मान्यता बा कि एह जगह के आधिकारिक पूजा सबसे पहिले होला। एकरा बाद ही देवघर के महादेव के पूजा होला।"'''''<ref>{{Cite web |title=Ajgaivinath Temple, Bihar - Info, Timings, Photos, History |url=https://www.templepurohit.com/hindu-temple/ajgaivinath-temple/ |access-date=2023-07-27 |website=TemplePurohit - Your Spiritual Destination {{!}} Bhakti, Shraddha Aur Ashirwad |language=en-US }}{{Dead link|date=September 2025 |bot=InternetArchiveBot |fix-attempted=yes }}</ref>
== देवता आ मान्यता के बारे में बतावल गइल बा ==
'''अजगैबीनाथ मंदिर''' में पूजल जाए वाला मुख्य देवता भगवान शिव हवें जिनके अजगैबीनाथ के नाँव से भी जानल जाला। हिन्दू पौराणिक कथा सभ के अनुसार ई मानल जाला कि [[शिव|भगवान शिव]] [[अमरनाथ गुफा मंदिर|अमरनाथ गुफा]] के यात्रा के दौरान कुछ समय ले इहाँ निवास कइले रहलें। मंदिर के ऐतिहासिक आ धार्मिक महत्व साल भर में खासतौर पर भगवान शिव के समर्पित विशेष अवसर आ परब के दौरान बहुत संख्या में भक्त लोग के आकर्षित करे ला।<ref>{{Cite web |last=Tourism |first=Bihar |date=30 January 2023 |title=Ajgaibinath Temple |url=https://tourism.bihar.gov.in/en/destinations/bhagalpur/ajgaibinath-temple |access-date=20 July 2023 |website=tourism.bihar.gov.in |archive-date=21 July 2023 |archive-url=https://web.archive.org/web/20230721141903/https://tourism.bihar.gov.in/en/destinations/bhagalpur/ajgaibinath-temple |url-status=dead }}</ref>
== श्रावणी मेला अउर काँवर यात्रा ==
हर साल लाखों तीर्थयात्री एह तीर्थ में आवेले। ई श्रावण (हिन्दू कैलेंडर के एक महीना) के मेला खातिर परसिद्ध बा, जुलाई से अगस्त के बीच। भारत के विभिन्न भाग से लगभग 8 से 1 करोड़ श्रद्धालु लोग एह जगह पर आवेला आ अजगैबीनाथ मंदिर के किनारे सुल्तानगंज से एकट्ठा कइल देवता के गंडक के पवित्र जल ('''उत्तरवाहिनी गंगा''') चढ़ावेला आ [[शिव|'''अजगैबीनाथ (भगवान शिव)''']] के प्रार्थना करेला जवन लगभग... देवघर आ बैद्यनाथ से 108 किलोमीटर दूर बा। पानी भी कानवरिया लोग ले आवेला, जे कवड़ी में पानी के ढो के चलेला, आ पूरा दूरी, नंगे पांव चलेला। पूरा रास्ता पानी लेके चलत बड़हन भीड़ मिली। केसर से रंगल कपड़ा में लोग के अटूट लाइन महीना भर पूरा 108 किलोमीटर में फइलल बा। तीर्थयात्री लोग के डाक बम कहल जाला आ [[भागलपुर जिला]] में स्थित अजगैबीनाथ , सुल्तानगुंज से '''वैद्यनाथ''' के यात्रा में एक बेर भी ना रुकेले . बाद में मंदिर के तीर्थयात्री लोग '''बासुकीनाथ मंदिर''' के दौरा करेला।<ref>{{Cite web |date=2011-08-11 |title=Administration gears up for Shravani Fair - Times Of India |url=http://articles.timesofindia.indiatimes.com/2004-05-30/patna/28348050_1_shrawani-bhagalpur-high-level-meeting |access-date=2023-07-22 |website=web.archive.org |archive-date=2011-08-11 |archive-url=https://web.archive.org/web/20110811055452/http://articles.timesofindia.indiatimes.com/2004-05-30/patna/28348050_1_shrawani-bhagalpur-high-level-meeting |url-status=dead }}</ref><ref>{{Cite web |date=2011-08-11 |title=Month-long Shrawani Mela ends - Times Of India |url=http://articles.timesofindia.indiatimes.com/2009-08-06/ranchi/28156469_1_kumbh-mela-lakh-pilgrims-shrawani-mela |url-status=dead |access-date=2023-07-22 |website=web.archive.org |archive-date=2011-08-11 |archive-url=https://web.archive.org/web/20110811055512/http://articles.timesofindia.indiatimes.com/2009-08-06/ranchi/28156469_1_kumbh-mela-lakh-pilgrims-shrawani-mela }}</ref><ref>{{Cite web |date=2023-07-17 |title=PHOTOS: अजगैबीनाथ मंदिर में सरकारी पूजा की देखें तस्वीर, सोमवार को सुल्तानगंज में कांवरियों का दिखा हुजूम |url=https://www.prabhatkhabar.com/photos/shravani-mela-2023-somwari-puja-photos-of-ajgaibinath-mandir-sultanganj-kanwariya-path-skt |access-date=2023-07-22 |website=Prabhat Khabar |language=hi}}</ref>
== गैलरी में बा ==
[[चित्र:Ajgaibinath_temple_Day-View.png|center|thumb|276x276px| अजगैबीनाथ मंदिर के दिन-दृश्य]]
== इहो देखल जाव ==
* [[बाबा धाम]]
* [[माँ जलेश्वरी मंदिर]]
== संदर्भ दिहल गइल बा ==
{{Reflist}}
[[श्रेणी:बिहार के पर्यटक आकर्षण]]
[[श्रेणी:शिव मंदिर]]
[[श्रेणी:बिहार के हिंदू मंदिर]]
[[श्रेणी:हिंदू मंदिर]]
nevs9ux9kmi213nuxq7d3s47fefynte
2024 के भारतीय आम चुनाव
0
92022
803373
785049
2026-07-29T16:24:37Z
SM7Bot
7808
/* top */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803373
wikitext
text/x-wiki
{{Infobox election
|country = भारत
|type = संसदीय
|election_name = भारतीय आम चुनाव,२०२४
|seats_for_election = लोक सभा की सभी 543 सीटे
|previous_election = भारतीय आम चुनाव, 2019
|previous_year = २०१९
|next_election = भारतीय आम चुनाव, 2024
|next_year = २०२९
|election_date = अप्रैल-मई २०२४
|map_image = Wahlkreise in Indien.svg
|map_caption = क्षेत्र के हिसाब से सीट। चुकी इ एफपीटीपी चुनाव ह एहसे सीट के योग हर पार्टी के कुल वोट हिस्सेदारी के अनुपात में ना तय कइल जाला, बालुक हर क्षेत्र में बहुलता के आधार प तय कइल जाला।
|opinion_polls = <!--सन् २०२४ के भारतीय आम चुनाव से सम्बन्धित जनमत सर्वेक्षण-->
|majority_seats = २७२
|previous_mps = १७वीं लोक सभा के सदस्यों की सूची
|elected_mps = <!--१८वीं लोक सभा के सदस्यों की सूची-->
|ongoing = नहीं
|image1 = Official Photograph of Prime Minister Narendra Modi Portrait.png
|leader1 = [[नरेंद्र मोदी]]
|leaders_seat1 = [[वाराणसी लोक सभा निर्वाचन क्षेत्र|वाराणसी]]
|seats1 =
|party1 = [[भारतीय जनता पार्टी|भाजपा]]
|alliance1 = [[राष्ट्रीय जनतांत्रिक गठबंधन|राजग]]
|percentage1 =
|last_election1 = ३०३ सीटें
|popular_vote1 =
|seat_change1 =
|seats_before1 =
|swing1 =
|image2 = Mallikarjun Kharge briefing the media after presenting the Interim Railway Budget 2014-15 in New Delhi (cropped).jpg
|leader2 = [[राहुल गांधी]]
|leaders_seat2 = [[वायनाड लोक सभा निर्वाचन क्षेत्र|वायनाड]]
|seats2 =
|party2 = [[भारतीय राष्ट्रीय कांग्रेस|कांग्रेस]]
|alliance2 = [[इंडियन नेशनल डेवलपमेंटल इंक्लूसिव अलायंस|इंनेडेइंअ]]
|percentage2 =
|last_election2 = ५२ सीटें
|popular_vote2 =
|seat_change2 =
|seats_before2 =
|swing2 =
|before_party = [[भारतीय जनता पार्टी|भाजपा]]
|before_election = [[नरेंद्र मोदी]]
|title = [[भारत के परधानमंत्री|प्रधानमंत्री]]
|after_election =
|after_party =
|posttitle = [[भारत के परधानमंत्री|प्रधानमंत्री]] चुनाव के बाद
}}
'''भारतीय आम चुनाव''' मई 2024 में 18वीं लोकसभा के सदस्य चुने के होवे वाला बा। वर्तमान प्रधानमंत्री भारतीय जनता पार्टी के नरेंद्र मोदी बाड़े।
1ex1up8elght58yq1a3ud86ft1b2dnj
अँगुरियावल (सेक्स)
0
96889
803315
798946
2026-07-29T16:20:20Z
SM7Bot
7808
/* सुरक्षा आ यौन उत्पीड़न */ टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803315
wikitext
text/x-wiki
'''अँगुरियावल''' चाहे '''अँगुरी कइल''' हाथ के [[अँगुरी]] के इस्तेमाल से [[योनि]] जेकरा के [[बुर]] भी कहल जाला या फिर [[गुदा]] के उत्तेजित करे के क्रिया ह। ई खुद से अकेलहूँ कइल जा सके ला आ पार्टनरो के संघे कइल जा सके ला।
==सुरक्षा आ यौन उत्पीड़न==
आम तौर प अंगुरी कइल सुरक्षित सेक्स मानल जाला। बिना सहमति के यौन पैठ के कानूनी तौर प कइसे वर्गीकृत कइल जाला, इ अधिकार क्षेत्र प निर्भर करेला। उदाहरण खातिर, बिना सहमति के अँगुरी से योनि भा गुदा में घुसावल ऑस्ट्रेलिया में बलात्कार आ अमेरिका में जबरन बलात्कार हवे। स्कॉटलैंड में बलात्कार शब्द के इस्तेमाल खाली लिंग घुसे खातिर होला जबकि अँगुरी के घुसे से "पेनेट्रेटिव यौन उत्पीड़न" मानल जा सके ला।
==चित्र गैलरी==
[[चित्र:Wiki-fingering.png|thumb|पुरुष द्वारा औरत के अँगुरी कइल।]]
[[चित्र:Solo fingering.jpg|thumb|खुद से अँगुरी करत एगो औरत।]]
[[श्रेणी:सेक्स]]
[[श्रेणी:मानव कामुकता]]
7zx3xxn9u2nxouacvku1pvpcuin2xl4
गगन थापा
0
99931
803362
787434
2026-07-29T16:23:43Z
SM7Bot
7808
टाइपो फिक्स कइल गइल using [[Project:AWB|AWB]]
803362
wikitext
text/x-wiki
{{Infobox officeholder
| name = गगन थापा
| image = File:Gagan_Kumar_Thapa.jpg
| width = 200px
| caption = गगन थापा
| nickname =
| birth_name =
| birth_date = १६ जुलाई, १९४०
| birth_place =
| residence =
| alma_mater = [[त्रि-चन्द्र कलेज|त्रिचन्द्र कलेज]]
| office = [[नेपाली कांग्रेसके सभापति]]
| termstart = १४ जनवरी, २०२६
| termend =
| primeminister = <nowiki>[[सुशीला कार्की]</nowiki>
| president = [[विद्यादेवी भण्डारी]]
| predecessor = शेरबहादुर देउपा
| successor =
| office2 = सभासद काठमाडौँ निर्वाचन क्षेत्र ४
| predecessor2 = सुप्रभा घिमिरे
| occupation = राजनीति
| nationality = [[नेपाली]]
| religion = [[हिन्दू धर्म|हिन्दू]]
| spouse = डा. अन्जना केसी थापा
| awards =
| website = http://www.gaganthapa.com.np
}}
'''गगन थापा''' (पूरा नाम: ''गगन कुमार थापा'' ) [[नेपाली कांग्रेस]] के एगो राजनीतिज्ञ आ नेता बानी। एगो मुखर वक्ता थापा के राजनीतिक हलकन में [[नेपालके जनआन्दोलन २०६३|जनआन्दोलन-२]] में अपना भूमिका खातिर जानल जाला। <ref name="election.ujyaaloonline.com">http://election.ujyaaloonline.com/candidates/2249/Gagan-Thapa/ {{Webarchive|url=https://web.archive.org/web/20140802102322/http://election.ujyaaloonline.com/candidates/2249/Gagan-Thapa/|date=2014-08-02}}</ref> [https://english.onlinekhabar.com/gagan-thapa-nepali-congress-president.html नेपाली कांग्रेस के अध्यक्ष भी बानी। कांग्रेस के दूसरा विशेष महाधिवेशन के बाद उ कांग्रेस के अध्यक्ष बनले।]
== पृष्ठभूमि ==
थापा के जनम २०३३ विक्रम सम्बत में काठमांडू के '''एगो प्रसूति अस्पताल''' में भइल रहे। उनकर माता - पिता '''महेन्द्र कुमार थापा''' आ '''रामेश्वरी थापा''' हवें । उनकर दादा [[सोलुखुम्बु जिला]] के खाल्पा गाँव के धनी मुखिया भीमराज थापा हवें । इनकर जनम कश्यप कुल के गोदर थापा कुल में भइल रहे।
गगन के बचपन के कुछ समय [[सिंधुपाल्चोक जिला|सिंधुपालचौक जिला]] में बीतल । उनकर पिता महेन्द्र नेपाल विद्युत प्राधिकरण में इंजीनियर रहले । गगन के एगो बड़ भाई आ दू गो छोट भाई बाड़े। उनकर बड़ भाई पवन, जबकि छोट भाई सुमन आ जीवन। गगन सिद्धार्थ बनस्थली हाई स्कूल में पढ़ाई कइले रहले. ओहिजा पढ़त घरी उनकर इच्छा रहे कि डाक्टर-इंजीनियर बन जाव. उनकर परिवार आ स्कूल में उहे पढ़ावल जात रहे। गगन 2046 बीएस के जन आंदोलन में सक्रिय हो गए। ओह घरी उ ग्रेड 9 में पढ़त रहले, हालांकि राजनीति में आवे के कवनो इरादा ना रहे।
दूसरा संविधानसभा में कृषि आ जल संसाधन समिति के अध्यक्ष के रूप में प्रभावशाली भूमिका निभावे वाला थापा नेपाली कांग्रेस के सदस्य के रूप में हमेशा चर्चा में सबसे ऊपर रहे। समाजशास्त्र आ मानवशास्त्र में स्नातकोत्तर के पढ़ाई कइल थापा २०७८ से २०८२ ले नेपाली कांग्रेस के '''महासचिव''' के रूप में काम कइली। महा अधिवेशन में [[नेपाली कांग्रेस]] के प्रतिनिधि थापा के एगो अइसन नवही के रूप में जानल जाला जे राजा के सक्रिय शासन काल में कांग्रेस पार्टी के आवाज मजबूती से उठवले रहलें। दर्जनों बेर हिरासत में लिहल थापा सिनेमा देखल, पढ़ाई, घूमे-फिरे के शौक ह, बतवली।
संविधान सभा में नेपाली कांग्रेस किताब के लेखक थापा के बियाह नेपाली कांग्रेस नेता अर्जुन नरसिंह के सी के बेटी '''डॉ.''' '''अंजना के.सी.''' थापा संविधानसभा में दलन के बीच सहमति बनावे के मुख्य प्रयास होई बतवनी ।
== राजनीति में प्रवेश करीं ==
[[चित्र:Gagan_Thapa_performing_speech_in_Sydney.jpg|thumb|गगन थापा]]
त्रिचंद्र के कैंपस में पढ़ाई शुरू भईला के बाद उ छात्र संगठन में शामिल हो गइले। उ एनईएफ के सदस्य रहले। 2051 में त्रिचंद्र स्वतंत्र विद्यार्थी संघ के सदस्य चुनल गइलें। एकरा बाद उ राजनीति के माध्यम से आगे बढ़े के फैसला कइले। त्रिचंद्र सदस्य सचिव रहला के दौरान विद्यार्थी यूनियन के अध्यक्ष बनलें। 2055 बीएस में विद्यार्थी यूनियनके अध्यक्ष बनला के बाद उ एनईएफ के केंद्रीय राजनीति में शामिल हो गइले। उपाध्यक्ष आ महासचिव बन गइलन. 2051 में हमरा के जिनगी में पहिला बेर गिरफ्तार कइल गइल रहे। ओह घरी एमाले अल्पसंख्यक सरकार में रहे। गृहमंत्री केपी ओली मौजूद रहे। नेपाल विद्यार्थी संघ नेपाल बंद के आह्वान कइले रहे। आठ दस बेर जेल में बंद हो गइलन। 18 अक्टूबर 2059 के बाद जब सूर्य बहादुर थापा प्रधानमंत्री बनले त गुरुराज घिमिरे अवुरी पुरुषोत्तम पौडेल प भ्रष्टाचार के आरोप लागल। एकरा बाद उ आपन अधिकांश समय गली के आंदोलन में बितवले। 19 जनवरी के बाद गगन के देशद्रोह के आरोप में रिहा क दिहल गइल। ओकरा के गिरफ्तार क के छोड़ दिहल गइल। लोकतांत्रिक आंदोलन में उनकर मजबूत वक्तृत्व आ लोकप्रिय छवि के बहुते तारीफ भइल. हालांकि उ लोकतंत्र के नायक बने में सफल भईले। पार्टी के तेरहवाँ महासम्मेलन मे महासचिव पद खातिर उम्मेदवारी के घोषणा करेवाला थापा २०८२ मे भइल विशेष महाधिवेसनसे पार्टी सभापति निर्वाचित भ गइल बा ।
प्रचंड मंत्रिमंडल में स्वास्थ्य मंत्री बनले, 2073 बीएस .
== शादीशुदा जीवन के बा ==
उनकर पत्नी कांग्रेस नेता अर्जुन नरसिंह केसी के बेटी [[डा अञ्जना केसी|डॉ. अंजना केसी]] हई।
== प्रतिनिधि सभा के ह ==
२०७९ मे भइल प्रतिनिधि सभा के चुनाव में [[नेपाली कांग्रेस]] 21,302 <ref name="election.ujyaaloonline.com"/> वोट हासिल क के [[काठमांडो|काठमांडू के]] [[बागमती प्रदेश|बागमती प्रांत]] के प्रतिनिधि सभा क्षेत्र नंबर 4 में जीत हासिल कइलस। उनुकर निकटतम प्रतिद्वंद्वी नेपाल कम्युनिस्ट पार्टी के [[राजन भट्टराई]] १३,८५५ वोट हासिल कइले। <ref>{{Cite web |last=bizmandu.com |title=कांग्रेसमा गगन बने जित निकाल्ने पहिलो पदाधिकारी, 'देश हाँक्न नयाँ जोशका साथ लाग्छु' |url=https://www.bizmandu.com/content/20221122120446.html |access-date=2022-11-22 |website=www.bizmandu.com}}</ref> [[नेपाली कांग्रेस]] संसदीय दल के नेतृत्व खातिर [[शेरबहादुर देउवा|शेरबहादुर देउबा]] से 25 वोट से हार गइलें। <ref>{{Cite web |title=उम्मेदवारी दर्ता गराउन देउवा सिंहदरबारमा |url=https://annapurnapost.com/news/deuba-singh-darbar-to-register-candidacy-for-the-leader-of-the-parliamentary-party-220750 |access-date=2022-12-21 |website=उम्मेदवारी दर्ता गराउन देउवा सिंहदरबारमा |language=en}}</ref> थापा प्रधानमंत्री बने के मकसद से मिशन पीएम अभियान चलावत आइल बाड़ें।
== संदर्भ दिहल गइल बा ==
{{Reflist}}
[[श्रेणी:नेपाल के राजनेता]]
[[श्रेणी:नेपाल के पूर्वमन्त्री]]
[[श्रेणी:नेपाल के राजनीति]]
72wowh2ut4igqosn2bz3qjql13n8xft
Module:InfoboxImage/data
828
100982
803571
2025-10-18T02:04:40Z
en>Hike395
0
factor out data
803571
Scribunto
text/plain
local placeholder_image = {
"Blue - Replace this image female.svg",
"Blue - Replace this image male.svg",
"Flag of None (square).svg",
"Flag of None.svg",
"Flag of.svg",
"Green - Replace this image female.svg",
"Green - Replace this image male.svg",
"Image is needed female.svg",
"Image is needed male.svg",
"Location map of None.svg",
"Male no free image yet.png",
"Missing flag.png",
"No flag.svg",
"No free portrait.svg",
"No portrait (female).svg",
"No portrait (male).svg",
"Red - Replace this image female.svg",
"Red - Replace this image male.svg",
"Replace this image female.svg",
"Replace this image male (blue).svg",
"Replace this image male.svg",
"Silver - Replace this image female.svg",
"Silver - Replace this image male.svg",
"Replace this image.svg",
"Cricket no pic.png",
"CarersLogo.gif",
"Diagram Needed.svg",
"Example.jpg",
"Image placeholder.png",
"No male portrait.svg",
"Nocover-upload.png",
"NoDVDcover copy.png",
"Noribbon.svg",
"No portrait-BFD-test.svg",
"Placeholder barnstar ribbon.png",
"Project Trains no image.png",
"Image-request.png",
"Sin bandera.svg",
"Sin escudo.svg",
"Replace this image - temple.png",
"Replace this image butterfly.png",
"Replace this image.svg",
"Replace this image1.svg",
"Resolution angle.png",
"Image-No portrait-text-BFD-test.svg",
"Insert image here.svg",
"No image available.png",
"NO IMAGE YET square.png",
"NO IMAGE YET.png",
"No Photo Available.svg",
"No Screenshot.svg",
"No-image-available.jpg",
"Null.png",
"PictureNeeded.gif",
"Place holder.jpg",
"Unbenannt.JPG",
"UploadACopyrightFreeImage.svg",
"UploadAnImage.gif",
"UploadAnImage.svg",
"UploadAnImageShort.svg",
"CarersLogo.gif",
"Diagram Needed.svg",
"No male portrait.svg",
"NoDVDcover copy.png",
"Placeholder barnstar ribbon.png",
"Project Trains no image.png",
"Image-request.png",
"Noimage.gif",
}
local categories = {
url_image_links = "[[Category:Pages using infoboxes with URL in image parameter]]",
thumbnail_images = "[[Category:Pages using infoboxes with thumbnail images]]",
}
return { placeholder_image = placeholder_image, categories = categories }
aiyqe7ro9krvvm7ujhmh89pgy3x0il5
803572
803571
2025-10-18T02:15:57Z
en>Hike395
0
return hash table not list
803572
Scribunto
text/plain
local placeholderList = {
"Blue - Replace this image female.svg",
"Blue - Replace this image male.svg",
"Flag of None (square).svg",
"Flag of None.svg",
"Flag of.svg",
"Green - Replace this image female.svg",
"Green - Replace this image male.svg",
"Image is needed female.svg",
"Image is needed male.svg",
"Location map of None.svg",
"Male no free image yet.png",
"Missing flag.png",
"No flag.svg",
"No free portrait.svg",
"No portrait (female).svg",
"No portrait (male).svg",
"Red - Replace this image female.svg",
"Red - Replace this image male.svg",
"Replace this image female.svg",
"Replace this image male (blue).svg",
"Replace this image male.svg",
"Silver - Replace this image female.svg",
"Silver - Replace this image male.svg",
"Replace this image.svg",
"Cricket no pic.png",
"CarersLogo.gif",
"Diagram Needed.svg",
"Example.jpg",
"Image placeholder.png",
"No male portrait.svg",
"Nocover-upload.png",
"NoDVDcover copy.png",
"Noribbon.svg",
"No portrait-BFD-test.svg",
"Placeholder barnstar ribbon.png",
"Project Trains no image.png",
"Image-request.png",
"Sin bandera.svg",
"Sin escudo.svg",
"Replace this image - temple.png",
"Replace this image butterfly.png",
"Replace this image.svg",
"Replace this image1.svg",
"Resolution angle.png",
"Image-No portrait-text-BFD-test.svg",
"Insert image here.svg",
"No image available.png",
"NO IMAGE YET square.png",
"NO IMAGE YET.png",
"No Photo Available.svg",
"No Screenshot.svg",
"No-image-available.jpg",
"Null.png",
"PictureNeeded.gif",
"Place holder.jpg",
"Unbenannt.JPG",
"UploadACopyrightFreeImage.svg",
"UploadAnImage.gif",
"UploadAnImage.svg",
"UploadAnImageShort.svg",
"CarersLogo.gif",
"Diagram Needed.svg",
"No male portrait.svg",
"NoDVDcover copy.png",
"Placeholder barnstar ribbon.png",
"Project Trains no image.png",
"Image-request.png",
"Noimage.gif",
}
local placeholder_image = {}
for _, val in ipairs(placeholderList) do
placeholder_image[val] = true
end
local categories = {
url_image_links = "[[Category:Pages using infoboxes with URL in image parameter]]",
thumbnail_images = "[[Category:Pages using infoboxes with thumbnail images]]",
}
return { placeholder_image = placeholder_image, categories = categories }
4h39dpz9m6jxmu0vznaup0kyz6rbh4y
803573
803572
2025-10-18T09:43:07Z
en>WOSlinker
0
Protected "[[Module:InfoboxImage/data]]": [[WP:High-risk templates|High-risk template or module]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
803572
Scribunto
text/plain
local placeholderList = {
"Blue - Replace this image female.svg",
"Blue - Replace this image male.svg",
"Flag of None (square).svg",
"Flag of None.svg",
"Flag of.svg",
"Green - Replace this image female.svg",
"Green - Replace this image male.svg",
"Image is needed female.svg",
"Image is needed male.svg",
"Location map of None.svg",
"Male no free image yet.png",
"Missing flag.png",
"No flag.svg",
"No free portrait.svg",
"No portrait (female).svg",
"No portrait (male).svg",
"Red - Replace this image female.svg",
"Red - Replace this image male.svg",
"Replace this image female.svg",
"Replace this image male (blue).svg",
"Replace this image male.svg",
"Silver - Replace this image female.svg",
"Silver - Replace this image male.svg",
"Replace this image.svg",
"Cricket no pic.png",
"CarersLogo.gif",
"Diagram Needed.svg",
"Example.jpg",
"Image placeholder.png",
"No male portrait.svg",
"Nocover-upload.png",
"NoDVDcover copy.png",
"Noribbon.svg",
"No portrait-BFD-test.svg",
"Placeholder barnstar ribbon.png",
"Project Trains no image.png",
"Image-request.png",
"Sin bandera.svg",
"Sin escudo.svg",
"Replace this image - temple.png",
"Replace this image butterfly.png",
"Replace this image.svg",
"Replace this image1.svg",
"Resolution angle.png",
"Image-No portrait-text-BFD-test.svg",
"Insert image here.svg",
"No image available.png",
"NO IMAGE YET square.png",
"NO IMAGE YET.png",
"No Photo Available.svg",
"No Screenshot.svg",
"No-image-available.jpg",
"Null.png",
"PictureNeeded.gif",
"Place holder.jpg",
"Unbenannt.JPG",
"UploadACopyrightFreeImage.svg",
"UploadAnImage.gif",
"UploadAnImage.svg",
"UploadAnImageShort.svg",
"CarersLogo.gif",
"Diagram Needed.svg",
"No male portrait.svg",
"NoDVDcover copy.png",
"Placeholder barnstar ribbon.png",
"Project Trains no image.png",
"Image-request.png",
"Noimage.gif",
}
local placeholder_image = {}
for _, val in ipairs(placeholderList) do
placeholder_image[val] = true
end
local categories = {
url_image_links = "[[Category:Pages using infoboxes with URL in image parameter]]",
thumbnail_images = "[[Category:Pages using infoboxes with thumbnail images]]",
}
return { placeholder_image = placeholder_image, categories = categories }
4h39dpz9m6jxmu0vznaup0kyz6rbh4y
803574
796827
2026-07-30T00:18:12Z
SM7
3953
3 revisions imported from [[:en:Module:InfoboxImage/data]]
796826
Scribunto
text/plain
local placeholderList = {
"Blue - Replace this image female.svg",
"Blue - Replace this image male.svg",
"Flag of None (square).svg",
"Flag of None.svg",
"Flag of.svg",
"Green - Replace this image female.svg",
"Green - Replace this image male.svg",
"Image is needed female.svg",
"Image is needed male.svg",
"Location map of None.svg",
"Male no free image yet.png",
"Missing flag.png",
"No flag.svg",
"No free portrait.svg",
"No portrait (female).svg",
"No portrait (male).svg",
"Red - Replace this image female.svg",
"Red - Replace this image male.svg",
"Replace this image female.svg",
"Replace this image male (blue).svg",
"Replace this image male.svg",
"Silver - Replace this image female.svg",
"Silver - Replace this image male.svg",
"Replace this image.svg",
"Cricket no pic.png",
"CarersLogo.gif",
"Diagram Needed.svg",
"Example.jpg",
"Image placeholder.png",
"No male portrait.svg",
"Nocover-upload.png",
"NoDVDcover copy.png",
"Noribbon.svg",
"No portrait-BFD-test.svg",
"Placeholder barnstar ribbon.png",
"Project Trains no image.png",
"Image-request.png",
"Sin bandera.svg",
"Sin escudo.svg",
"Replace this image - temple.png",
"Replace this image butterfly.png",
"Replace this image.svg",
"Replace this image1.svg",
"Resolution angle.png",
"Image-No portrait-text-BFD-test.svg",
"Insert image here.svg",
"No image available.png",
"NO IMAGE YET square.png",
"NO IMAGE YET.png",
"No Photo Available.svg",
"No Screenshot.svg",
"No-image-available.jpg",
"Null.png",
"PictureNeeded.gif",
"Place holder.jpg",
"Unbenannt.JPG",
"UploadACopyrightFreeImage.svg",
"UploadAnImage.gif",
"UploadAnImage.svg",
"UploadAnImageShort.svg",
"CarersLogo.gif",
"Diagram Needed.svg",
"No male portrait.svg",
"NoDVDcover copy.png",
"Placeholder barnstar ribbon.png",
"Project Trains no image.png",
"Image-request.png",
"Noimage.gif",
}
local placeholder_image = {}
for _, val in ipairs(placeholderList) do
placeholder_image[val] = true
end
local categories = {
url_image_links = "[[Category:Pages using infoboxes with URL in image parameter]]",
thumbnail_images = "[[Category:Pages using infoboxes with thumbnail images]]",
}
return { placeholder_image = placeholder_image, categories = categories }
4h39dpz9m6jxmu0vznaup0kyz6rbh4y
विकिपीडिया:आँकड़ा सभ/२०२६/जुलाई
4
101100
803396
803262
2026-07-29T19:21:32Z
NeechalBOT
7874
statistics
803396
wikitext
text/x-wiki
<!--- stats starts--->{{प्रयोगकर्ता:Neechalkaran/statnotice}}{| class="wikitable sortable" style="width:90%"
|-
! Date(Time)
! Pages
! Articles
! Edits
! Users
! Files
! Activeusers
{{User:Neechalkaran/template/daily
|Date =१-७-२०२६
|Pages = 81892
|dPages = 4
|Articles = 9097
|dArticles = 0
|Edits = 795873
|dEdits = 9
|Files = 54
|dFiles = 0
|Users = 40208
|dUsers = 5
|Ausers = 57
|dAusers = -4
}}
{{User:Neechalkaran/template/daily
|Date =२-७-२०२६
|Pages = 81895
|dPages = 3
|Articles = 9097
|dArticles = 0
|Edits = 795882
|dEdits = 9
|Files = 54
|dFiles = 0
|Users = 40218
|dUsers = 10
|Ausers = 57
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =३-७-२०२६
|Pages = 81895
|dPages = 0
|Articles = 9097
|dArticles = 0
|Edits = 795890
|dEdits = 8
|Files = 54
|dFiles = 0
|Users = 40227
|dUsers = 9
|Ausers = 57
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =४-७-२०२६
|Pages = 81895
|dPages = 0
|Articles = 9096
|dArticles = -1
|Edits = 795904
|dEdits = 14
|Files = 54
|dFiles = 0
|Users = 40231
|dUsers = 4
|Ausers = 59
|dAusers = 2
}}
{{User:Neechalkaran/template/daily
|Date =५-७-२०२६
|Pages = 81902
|dPages = 7
|Articles = 9098
|dArticles = 2
|Edits = 795944
|dEdits = 40
|Files = 54
|dFiles = 0
|Users = 40236
|dUsers = 5
|Ausers = 59
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =६-७-२०२६
|Pages = 81907
|dPages = 5
|Articles = 9101
|dArticles = 3
|Edits = 796019
|dEdits = 75
|Files = 54
|dFiles = 0
|Users = 40240
|dUsers = 4
|Ausers = 59
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =७-७-२०२६
|Pages = 81916
|dPages = 9
|Articles = 9103
|dArticles = 2
|Edits = 796069
|dEdits = 50
|Files = 54
|dFiles = 0
|Users = 40248
|dUsers = 8
|Ausers = 63
|dAusers = 4
}}
{{User:Neechalkaran/template/daily
|Date =८-७-२०२६
|Pages = 81920
|dPages = 4
|Articles = 9104
|dArticles = 1
|Edits = 796087
|dEdits = 18
|Files = 54
|dFiles = 0
|Users = 40255
|dUsers = 7
|Ausers = 63
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =९-७-२०२६
|Pages = 81925
|dPages = 5
|Articles = 9106
|dArticles = 2
|Edits = 796126
|dEdits = 39
|Files = 54
|dFiles = 0
|Users = 40261
|dUsers = 6
|Ausers = 63
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =१०-७-२०२६
|Pages = 81928
|dPages = 3
|Articles = 9109
|dArticles = 3
|Edits = 796172
|dEdits = 46
|Files = 54
|dFiles = 0
|Users = 40265
|dUsers = 4
|Ausers = 64
|dAusers = 1
}}
{{User:Neechalkaran/template/daily
|Date =११-७-२०२६
|Pages = 81931
|dPages = 3
|Articles = 9109
|dArticles = 0
|Edits = 796343
|dEdits = 171
|Files = 54
|dFiles = 0
|Users = 40270
|dUsers = 5
|Ausers = 64
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =१२-७-२०२६
|Pages = 81937
|dPages = 6
|Articles = 9112
|dArticles = 3
|Edits = 796450
|dEdits = 107
|Files = 54
|dFiles = 0
|Users = 40277
|dUsers = 7
|Ausers = 64
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =१३-७-२०२६
|Pages = 81962
|dPages = 25
|Articles = 9118
|dArticles = 6
|Edits = 796848
|dEdits = 398
|Files = 54
|dFiles = 0
|Users = 40283
|dUsers = 6
|Ausers = 66
|dAusers = 2
}}
{{User:Neechalkaran/template/daily
|Date =१४-७-२०२६
|Pages = 81963
|dPages = 1
|Articles = 9118
|dArticles = 0
|Edits = 796951
|dEdits = 103
|Files = 54
|dFiles = 0
|Users = 40292
|dUsers = 9
|Ausers = 66
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =१५-७-२०२६
|Pages = 81966
|dPages = 3
|Articles = 9119
|dArticles = 1
|Edits = 796982
|dEdits = 31
|Files = 54
|dFiles = 0
|Users = 40295
|dUsers = 3
|Ausers = 66
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =१६-७-२०२६
|Pages = 81976
|dPages = 10
|Articles = 9121
|dArticles = 2
|Edits = 797197
|dEdits = 215
|Files = 54
|dFiles = 0
|Users = 40297
|dUsers = 2
|Ausers = 66
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =१७-७-२०२६
|Pages = 81994
|dPages = 18
|Articles = 9126
|dArticles = 5
|Edits = 797326
|dEdits = 129
|Files = 54
|dFiles = 0
|Users = 40301
|dUsers = 4
|Ausers = 66
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =१८-७-२०२६
|Pages = 82002
|dPages = 8
|Articles = 9128
|dArticles = 2
|Edits = 797384
|dEdits = 58
|Files = 54
|dFiles = 0
|Users = 40305
|dUsers = 4
|Ausers = 66
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =१९-७-२०२६
|Pages = 82011
|dPages = 9
|Articles = 9129
|dArticles = 1
|Edits = 797820
|dEdits = 436
|Files = 54
|dFiles = 0
|Users = 40309
|dUsers = 4
|Ausers = 45
|dAusers = -21
}}
{{User:Neechalkaran/template/daily
|Date =२०-७-२०२६
|Pages = 82025
|dPages = 14
|Articles = 9132
|dArticles = 3
|Edits = 798488
|dEdits = 668
|Files = 54
|dFiles = 0
|Users = 40310
|dUsers = 1
|Ausers = 45
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =२१-७-२०२६
|Pages = 82026
|dPages = 1
|Articles = 9133
|dArticles = 1
|Edits = 798514
|dEdits = 26
|Files = 54
|dFiles = 0
|Users = 40315
|dUsers = 5
|Ausers = 45
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =२२-७-२०२६
|Pages = 82037
|dPages = 11
|Articles = 9138
|dArticles = 5
|Edits = 798566
|dEdits = 52
|Files = 54
|dFiles = 0
|Users = 40323
|dUsers = 8
|Ausers = 44
|dAusers = -1
}}
{{User:Neechalkaran/template/daily
|Date =२३-७-२०२६
|Pages = 82053
|dPages = 16
|Articles = 9143
|dArticles = 5
|Edits = 798618
|dEdits = 52
|Files = 54
|dFiles = 0
|Users = 40326
|dUsers = 3
|Ausers = 44
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =२४-७-२०२६
|Pages = 82054
|dPages = 1
|Articles = 9143
|dArticles = 0
|Edits = 798630
|dEdits = 12
|Files = 54
|dFiles = 0
|Users = 40330
|dUsers = 4
|Ausers = 44
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =२५-७-२०२६
|Pages = 82084
|dPages = 30
|Articles = 9146
|dArticles = 3
|Edits = 799457
|dEdits = 827
|Files = 54
|dFiles = 0
|Users = 40336
|dUsers = 6
|Ausers = 45
|dAusers = 1
}}
{{User:Neechalkaran/template/daily
|Date =२६-७-२०२६
|Pages = 82099
|dPages = 15
|Articles = 9152
|dArticles = 6
|Edits = 799564
|dEdits = 107
|Files = 54
|dFiles = 0
|Users = 40346
|dUsers = 10
|Ausers = 45
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =२७-७-२०२६
|Pages = 82101
|dPages = 2
|Articles = 9152
|dArticles = 0
|Edits = 799603
|dEdits = 39
|Files = 54
|dFiles = 0
|Users = 40350
|dUsers = 4
|Ausers = 45
|dAusers = 0
}}
{{User:Neechalkaran/template/daily
|Date =२८-७-२०२६
|Pages = 82114
|dPages = 13
|Articles = 9156
|dArticles = 4
|Edits = 800027
|dEdits = 424
|Files = 54
|dFiles = 0
|Users = 40358
|dUsers = 8
|Ausers = 46
|dAusers = 1
}}
{{User:Neechalkaran/template/daily
|Date =२९-७-२०२६
|Pages = 82126
|dPages = 12
|Articles = 9160
|dArticles = 4
|Edits = 800303
|dEdits = 276
|Files = 54
|dFiles = 0
|Users = 40363
|dUsers = 5
|Ausers = 46
|dAusers = 0
}}
<!---Place new stats here--->
|}
<!--- stats ends--->
7lknuqa9cxubzphcvurc99ih80nzpv8
Module:Deprecated
828
101239
803410
2025-03-21T01:53:59Z
en>Awesome Aasim
0
module to mark modules or member items as deprecated
803410
Scribunto
text/plain
--- Marks functions as deprecated, and provides a warning when they are called.
-- @release alpha
-- @author [[User:Awesome_Aasim|Awesome Aasim]]
-- @function deprecated
-- @param {table} p package frame
-- @param deprecatedTable
-- @return package
return function(p, deprecatedTable, replacement)
local pckg = {}
--- Warn
-- @param {string} text warning text
function warn(text)
local tb = debug.traceback()
mw.log(text .. '\n' .. tb)
mw.addWarning(text .. tb:gsub("\n", "<br/>"):gsub("\t", " "))
end
if deprecatedTable == nil or deprecatedTable == true then
deprecatedTable = {}
for k,_ in pairs(p) do
deprecatedTable[k] = {
deprecated = true,
replacement = replacement or ""
}
end
end
setmetatable(pckg, {
__index = function(t, index)
if deprecatedTable[index] and deprecatedTable[index]["deprecated"] then
warn(
mw.ustring.format(
"Deprecated member <code>%s</code> called ", index
) .. (
deprecatedTable[index]["replacement"]
and mw.ustring.format("Please %s instead.", deprecatedTable[index]["replacement"])
or ''
)
)
end
return p[index]
end
})
return pckg
end
ssbisxzfz9d69ye8woy3nnh2smfoesa
803411
803410
2025-03-27T23:26:07Z
en>Awesome Aasim
0
803411
Scribunto
text/plain
--- Marks functions as deprecated, and provides a warning when they are called.
-- @release alpha
-- @author [[User:Awesome_Aasim|Awesome Aasim]]
-- @function deprecated
-- @param {table} p package frame
-- @param deprecatedTable
-- @return package
return function(p, deprecatedTable, replacement)
local pckg = {}
--- Warn
-- @param {string} text warning text
function warn(text)
local tb = debug.traceback()
mw.log(text .. '\n' .. tb)
mw.addWarning(text .. tb:gsub("\n", "<br/>"):gsub("\t", " "))
end
if deprecatedTable == nil or deprecatedTable == true then
deprecatedTable = {}
for k,_ in pairs(p) do
deprecatedTable[k] = {
deprecated = true,
replacement = replacement or ""
}
end
end
setmetatable(pckg, {
__index = function(t, index)
if deprecatedTable[index] and deprecatedTable[index]["deprecated"] then
warn(
mw.ustring.format(
"Deprecated member <code>%s</code> called. ", index
) .. (
deprecatedTable[index]["replacement"]
and mw.ustring.format("Please %s instead.", deprecatedTable[index]["replacement"])
or ''
)
)
end
return p[index]
end
})
return pckg
end
r4g4p77ei7eef8z5ab5v120uucd4jkm
803412
803411
2025-03-30T15:49:00Z
en>Awesome Aasim
0
803412
Scribunto
text/plain
--- Marks items as deprecated, and provides a warning when they are called.
-- @release alpha
-- @author [[User:Awesome_Aasim|Awesome Aasim]]
-- @function deprecated
-- @param {table} p package frame
-- @param deprecatedTable
-- @return package
return function(p, deprecatedTable, replacement)
local pckg = {}
--- Warn
-- @param {string} text warning text
function warn(text)
local tb = debug.traceback()
mw.log(text .. '\n' .. tb)
mw.addWarning(text .. tb:gsub("\n", "<br/>"):gsub("\t", " "))
end
if deprecatedTable == nil or deprecatedTable == true then
deprecatedTable = {}
for k,_ in pairs(p) do
deprecatedTable[k] = {
deprecated = true,
replacement = replacement or ""
}
end
end
setmetatable(pckg, {
__index = function(t, index)
if deprecatedTable[index] and deprecatedTable[index]["deprecated"] then
warn(
mw.ustring.format(
"Deprecated member <code>%s</code> called. ", index
) .. (
deprecatedTable[index]["replacement"]
and mw.ustring.format("Please %s instead.", deprecatedTable[index]["replacement"])
or ''
)
)
end
return p[index]
end
})
return pckg
end
7ml5wv91a0i4zxlsitvw0bvoq1txcko
803413
801545
2025-05-27T19:00:29Z
en>Awesome Aasim
0
803413
Scribunto
text/plain
--- Marks items as deprecated, and provides a warning when they are called.
--
-- ## Limitations ##
-- This module will not provide a warning if a deprecated item is called from
-- within the same module that this module is used.
--
-- @release alpha
-- @author [[User:Awesome_Aasim|Awesome Aasim]]
-- @function deprecated
-- @param {table} p package frame
-- @param deprecatedTable
-- @return package
return function(p, deprecatedTable, replacement)
local pckg = {}
--- Warn
-- @param {string} text warning text
function warn(text)
local tb = debug.traceback()
mw.log(text .. '\n' .. tb)
mw.addWarning(text .. tb:gsub("\n", "<br/>"):gsub("\t", " "))
end
if deprecatedTable == nil or deprecatedTable == true then
deprecatedTable = {}
for k,_ in pairs(p) do
deprecatedTable[k] = {
deprecated = true,
replacement = replacement or ""
}
end
end
setmetatable(pckg, {
__index = function(t, index)
if deprecatedTable[index] and deprecatedTable[index]["deprecated"] then
warn(
mw.ustring.format(
"Deprecated member <code>%s</code> called. ", index
) .. (
deprecatedTable[index]["replacement"]
and mw.ustring.format("Please %s instead.", deprecatedTable[index]["replacement"])
or ''
)
)
end
return p[index]
end
})
return pckg
end
my1p1xp76qgfnvctx8pevv0cvz0hjyb
803414
801546
2026-07-29T23:43:35Z
SM7
3953
4 revisions imported from [[:en:Module:Deprecated]]
801545
Scribunto
text/plain
--- Marks items as deprecated, and provides a warning when they are called.
-- @release alpha
-- @author [[User:Awesome_Aasim|Awesome Aasim]]
-- @function deprecated
-- @param {table} p package frame
-- @param deprecatedTable
-- @return package
return function(p, deprecatedTable, replacement)
local pckg = {}
--- Warn
-- @param {string} text warning text
local function warn(text)
local tb = debug.traceback()
mw.log(text .. '\n' .. tb)
mw.addWarning(text .. tb:gsub("\n", "<br/>"):gsub("\t", " "))
end
if deprecatedTable == nil or deprecatedTable == true then
deprecatedTable = {}
for k,_ in pairs(p) do
deprecatedTable[k] = {
deprecated = true,
replacement = replacement or ""
}
end
end
setmetatable(pckg, {
__index = function(t, index)
if deprecatedTable[index] and deprecatedTable[index]["deprecated"] then
warn(
mw.ustring.format(
"Deprecated member <code>%s</code> called. ", index
) .. (
deprecatedTable[index]["replacement"]
and mw.ustring.format("Please %s instead.", deprecatedTable[index]["replacement"])
or ''
)
)
end
return p[index]
end
})
return pckg
end
022sv6wwuf8k0nkwfuwslws7f33h5rm
प्रयोगकर्ता वार्ता:Roopeank
3
101339
803285
2026-07-29T13:26:34Z
Ternarius
20240
Ternarius पन्ना [[प्रयोगकर्ता वार्ता:Roopeank]] के [[प्रयोगकर्ता वार्ता:LasseKeskinen]] पर स्थानांतरण कइलें: प्रयोगकर्ता के नाँव बदलाव के दौरान पन्ना "[[Special:CentralAuth/Roopeank|Roopeank]]" से "[[Special:CentralAuth/LasseKeskinen|LasseKeskinen]]" पर ऑटोमेटिक रूप से स्थानांतरित भइल।
803285
wikitext
text/x-wiki
#REDIRECT [[प्रयोगकर्ता वार्ता:LasseKeskinen]]
2vtlcb7aoiqgym2p7xzqwqwegq5vas9
अँजोर पाख
0
101340
803375
2026-07-29T17:39:40Z
SM7
3953
पन्ना [[पाख]] पर अनुप्रेषित कइल गइल
803375
wikitext
text/x-wiki
#REDIRECT [[पाख]]
g40w420yht93y905kz5oy3wc5k1bol2
803376
803375
2026-07-29T17:40:18Z
SM7
3953
Added {{[[:Template:R to related topic|R to related topic]]}} tag to redirect
803376
wikitext
text/x-wiki
#REDIRECT [[पाख]]
{{Redirect category shell|
{{R to related topic}}
}}
192loqddc2iljk6xfjdzz9hefcqrnsy
श्रेणी:भारतीय हिंदू संत
14
101341
803377
2026-07-29T17:41:29Z
SM7
3953
पन्ना बनावल गइल "[[श्रेणी:भारतीय संत|हिंदू]] [[श्रेणी:हिंदू संत]]" के साथ
803377
wikitext
text/x-wiki
[[श्रेणी:भारतीय संत|हिंदू]]
[[श्रेणी:हिंदू संत]]
r4n853xzhzm72snk21kgr8enbebzx0z
803378
803377
2026-07-29T17:42:46Z
SM7
3953
+commonscat
803378
wikitext
text/x-wiki
{{cc|Hindu saints from India}}
[[श्रेणी:भारतीय संत|हिंदू]]
[[श्रेणी:हिंदू संत]]
3nii0muqci976gua0kg1nuhzwrrwd9d
श्रेणी:भारतीय संत
14
101342
803379
2026-07-29T17:43:22Z
SM7
3953
पन्ना बनावल गइल "[[श्रेणी:संत]]" के साथ
803379
wikitext
text/x-wiki
[[श्रेणी:संत]]
jv43iok9fjy7qu4czush5hp9z0b88ia
श्रेणी:संत
14
101343
803380
2026-07-29T17:44:34Z
SM7
3953
पन्ना बनावल गइल "{{cc|Saints}} {{ctm}}" के साथ
803380
wikitext
text/x-wiki
{{cc|Saints}}
{{ctm}}
f0006dzlaeqinpcg8w28aj6jxywhs2m
803381
803380
2026-07-29T17:45:28Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा [[श्रेणी:धर्म]] जोड़ल गइल
803381
wikitext
text/x-wiki
{{cc|Saints}}
{{ctm}}
[[श्रेणी:धर्म]]
p2goxvzbsfkrwxqoksr13psbr9k0tgq
श्रेणी:भारतीय हिंदू धार्मिक नेता
14
101344
803382
2026-07-29T17:46:05Z
SM7
3953
पन्ना बनावल गइल "{{cc|Hindu religious leaders from India}}" के साथ
803382
wikitext
text/x-wiki
{{cc|Hindu religious leaders from India}}
72x9c5fzwofwbueoxqk6btyk9ai5j0t
803383
803382
2026-07-29T17:47:19Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा [[श्रेणी:भारत में हिंदू धर्म]] जोड़ल गइल
803383
wikitext
text/x-wiki
{{cc|Hindu religious leaders from India}}
[[श्रेणी:भारत में हिंदू धर्म]]
pmopfpu62kni2dn2j7tps16m5m6gkp0
अन्हार पाख
0
101345
803385
2026-07-29T17:49:49Z
SM7
3953
पन्ना [[पाख]] पर अनुप्रेषित कइल गइल
803385
wikitext
text/x-wiki
#REDIRECT [[पाख]]
g40w420yht93y905kz5oy3wc5k1bol2
803386
803385
2026-07-29T17:50:04Z
SM7
3953
Added {{[[:Template:R to related topic|R to related topic]]}} tag to redirect
803386
wikitext
text/x-wiki
#REDIRECT [[पाख]]
{{Redirect category shell|
{{R to related topic}}
}}
192loqddc2iljk6xfjdzz9hefcqrnsy
पौहारी बाबा
0
101346
803387
2026-07-29T18:03:23Z
SM7
3953
नया आधार लेख / अंग्रेजी विकिपीडिया से अनुबाद कइ के
803387
wikitext
text/x-wiki
'''पौहारी बाबा''' (1798–1898), जिनका के '''पवहारी''' नाँव से भी जानल जाला, एगो हिंदू संन्यासी आ संत रहलें। उनकर जनम [[जौनपुर जिला|जौनपुर]] के प्रेमापुर गाँव में एगो ब्राह्मण परिवार में भइल रहे। बचपन में ऊ अपना चाचा के देखरेख में पढ़ाई करे खातिर [[गाजीपुर]] गइलें। उनकर चाचा [[रामानुज]] भा श्री संप्रदाय के अनुयायी रहलें। पढ़ाई पूरा कइला के बाद पौहारी बाबा कई गो जगह के यात्रा कइलें। [[काठियावाड़]] के [[गिरनार]] में उनकरा के [[योग]] के दीक्षा मिलल।<ref name="Nikhilananda p44—45">{{Harvnb|Nikhilananda|1953|pp=44–45}}</ref>
ओकरा बाद ऊ गाजीपुर लवट अइलें आ अपना घरे में जमीन के नीचे एगो साधना-स्थल (गुफा जइसन आश्रम) बनवलें, जहाँ ऊ कई-कई दिन तक [[ध्यान]] आ योग के साधना करत रहलें। ऊ अपना नम्रता, विनम्र व्यवहार आ परोपकार के भावना खातिर जानल जात रहलें। कथा हवे कि, एक रात एगो चोर उनकर आश्रम में घुस गइल। जब चोर चोरी कइल सामान छोड़के भागे लागल, त नींद से जागला के बाद पौहारी बाबा ओकरा पाछे दौड़लें आ उल्टे ऊ सामान चोर के देवे के कोशिश कइलें। एह घटना के चोर पर एतना गहिर असर पड़ल कि बाद में ऊ संन्यासी बन गइल आ पौहारी बाबा के अनुयायी हो गइल।
1890 में [[स्वामी विवेकानंद]] गाजीपुर गइलें आ पौहारी बाबा से भेंट कइलें। [[सिस्टर निवेदिता]] के अनुसार, 1898 में पौहारी बाबा आग में जलके देह त्याग दिहलें। एह घटना के सामान्य रूप से आत्मदाह के रूप में देखल जाला।<ref name="Nikhilananda p44—45" />
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
== स्रोत ==
* {{cite book|last=Nikhilananda|first=Swami|title=Vivekânanda: a biography|url=http://www.vivekananda.net/PDFBooks/BiographybyNikhilananda.pdf|year=1953|publisher=Ramakrishna-Vivekananda Center|isbn=978-0-911206-25-8}}
sybw0vj5i8tsuq35f1jp59bsxbf9j07
803388
803387
2026-07-29T18:04:25Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा +[[श्रेणी:भारतीय हिंदू संत]]; +[[श्रेणी:भारतीय हिंदू योगी]]
803388
wikitext
text/x-wiki
'''पौहारी बाबा''' (1798–1898), जिनका के '''पवहारी''' नाँव से भी जानल जाला, एगो हिंदू संन्यासी आ संत रहलें। उनकर जनम [[जौनपुर जिला|जौनपुर]] के प्रेमापुर गाँव में एगो ब्राह्मण परिवार में भइल रहे। बचपन में ऊ अपना चाचा के देखरेख में पढ़ाई करे खातिर [[गाजीपुर]] गइलें। उनकर चाचा [[रामानुज]] भा श्री संप्रदाय के अनुयायी रहलें। पढ़ाई पूरा कइला के बाद पौहारी बाबा कई गो जगह के यात्रा कइलें। [[काठियावाड़]] के [[गिरनार]] में उनकरा के [[योग]] के दीक्षा मिलल।<ref name="Nikhilananda p44—45">{{Harvnb|Nikhilananda|1953|pp=44–45}}</ref>
ओकरा बाद ऊ गाजीपुर लवट अइलें आ अपना घरे में जमीन के नीचे एगो साधना-स्थल (गुफा जइसन आश्रम) बनवलें, जहाँ ऊ कई-कई दिन तक [[ध्यान]] आ योग के साधना करत रहलें। ऊ अपना नम्रता, विनम्र व्यवहार आ परोपकार के भावना खातिर जानल जात रहलें। कथा हवे कि, एक रात एगो चोर उनकर आश्रम में घुस गइल। जब चोर चोरी कइल सामान छोड़के भागे लागल, त नींद से जागला के बाद पौहारी बाबा ओकरा पाछे दौड़लें आ उल्टे ऊ सामान चोर के देवे के कोशिश कइलें। एह घटना के चोर पर एतना गहिर असर पड़ल कि बाद में ऊ संन्यासी बन गइल आ पौहारी बाबा के अनुयायी हो गइल।
1890 में [[स्वामी विवेकानंद]] गाजीपुर गइलें आ पौहारी बाबा से भेंट कइलें। [[सिस्टर निवेदिता]] के अनुसार, 1898 में पौहारी बाबा आग में जलके देह त्याग दिहलें। एह घटना के सामान्य रूप से आत्मदाह के रूप में देखल जाला।<ref name="Nikhilananda p44—45" />
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
== स्रोत ==
* {{cite book|last=Nikhilananda|first=Swami|title=Vivekânanda: a biography|url=http://www.vivekananda.net/PDFBooks/BiographybyNikhilananda.pdf|year=1953|publisher=Ramakrishna-Vivekananda Center|isbn=978-0-911206-25-8}}
[[श्रेणी:भारतीय हिंदू संत]]
[[श्रेणी:भारतीय हिंदू योगी]]
dxpydvgxkmhm6k9q776hbfhhoosm9ri
803389
803388
2026-07-29T18:10:18Z
SM7
3953
[[User:SM7/stubsorter|Stubsorter]] के मदद से {{Bio-stub}} जोड़ल गइल।
803389
wikitext
text/x-wiki
'''पौहारी बाबा''' (1798–1898), जिनका के '''पवहारी''' नाँव से भी जानल जाला, एगो हिंदू संन्यासी आ संत रहलें। उनकर जनम [[जौनपुर जिला|जौनपुर]] के प्रेमापुर गाँव में एगो ब्राह्मण परिवार में भइल रहे। बचपन में ऊ अपना चाचा के देखरेख में पढ़ाई करे खातिर [[गाजीपुर]] गइलें। उनकर चाचा [[रामानुज]] भा श्री संप्रदाय के अनुयायी रहलें। पढ़ाई पूरा कइला के बाद पौहारी बाबा कई गो जगह के यात्रा कइलें। [[काठियावाड़]] के [[गिरनार]] में उनकरा के [[योग]] के दीक्षा मिलल।<ref name="Nikhilananda p44—45">{{Harvnb|Nikhilananda|1953|pp=44–45}}</ref>
ओकरा बाद ऊ गाजीपुर लवट अइलें आ अपना घरे में जमीन के नीचे एगो साधना-स्थल (गुफा जइसन आश्रम) बनवलें, जहाँ ऊ कई-कई दिन तक [[ध्यान]] आ योग के साधना करत रहलें। ऊ अपना नम्रता, विनम्र व्यवहार आ परोपकार के भावना खातिर जानल जात रहलें। कथा हवे कि, एक रात एगो चोर उनकर आश्रम में घुस गइल। जब चोर चोरी कइल सामान छोड़के भागे लागल, त नींद से जागला के बाद पौहारी बाबा ओकरा पाछे दौड़लें आ उल्टे ऊ सामान चोर के देवे के कोशिश कइलें। एह घटना के चोर पर एतना गहिर असर पड़ल कि बाद में ऊ संन्यासी बन गइल आ पौहारी बाबा के अनुयायी हो गइल।
1890 में [[स्वामी विवेकानंद]] गाजीपुर गइलें आ पौहारी बाबा से भेंट कइलें। [[सिस्टर निवेदिता]] के अनुसार, 1898 में पौहारी बाबा आग में जलके देह त्याग दिहलें। एह घटना के सामान्य रूप से आत्मदाह के रूप में देखल जाला।<ref name="Nikhilananda p44—45" />
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
== स्रोत ==
* {{cite book|last=Nikhilananda|first=Swami|title=Vivekânanda: a biography|url=http://www.vivekananda.net/PDFBooks/BiographybyNikhilananda.pdf|year=1953|publisher=Ramakrishna-Vivekananda Center|isbn=978-0-911206-25-8}}
[[श्रेणी:भारतीय हिंदू संत]]
[[श्रेणी:भारतीय हिंदू योगी]]
{{Bio-stub}}
0kkah698wo47d6ozei5s83hynakg7tt
803390
803389
2026-07-29T18:11:49Z
SM7
3953
ज्ञानसंदूक जोड़ल गइल
803390
wikitext
text/x-wiki
{{Infobox Hindu leader
|religion = [[हिंदू धर्म]]
|birth_date = 1798
|birth_place = [[जौनपुर जिला|जौनपुर]]
|death_date = 1898
|death_place = [[गाजीपुर]] (उमिर 99-100)
|death_cause = दाह (आत्म-दाह)<ref name="Nikhilananda p44—45" />
|philosophy = [[हठयोग]]
|influenced = [[स्वामी विवेकानंद]]
}}
'''पौहारी बाबा''' (1798–1898), जिनका के '''पवहारी''' नाँव से भी जानल जाला, एगो हिंदू संन्यासी आ संत रहलें। उनकर जनम [[जौनपुर जिला|जौनपुर]] के प्रेमापुर गाँव में एगो ब्राह्मण परिवार में भइल रहे। बचपन में ऊ अपना चाचा के देखरेख में पढ़ाई करे खातिर [[गाजीपुर]] गइलें। उनकर चाचा [[रामानुज]] भा श्री संप्रदाय के अनुयायी रहलें। पढ़ाई पूरा कइला के बाद पौहारी बाबा कई गो जगह के यात्रा कइलें। [[काठियावाड़]] के [[गिरनार]] में उनकरा के [[योग]] के दीक्षा मिलल।<ref name="Nikhilananda p44—45">{{Harvnb|Nikhilananda|1953|pp=44–45}}</ref>
ओकरा बाद ऊ गाजीपुर लवट अइलें आ अपना घरे में जमीन के नीचे एगो साधना-स्थल (गुफा जइसन आश्रम) बनवलें, जहाँ ऊ कई-कई दिन तक [[ध्यान]] आ योग के साधना करत रहलें। ऊ अपना नम्रता, विनम्र व्यवहार आ परोपकार के भावना खातिर जानल जात रहलें। कथा हवे कि, एक रात एगो चोर उनकर आश्रम में घुस गइल। जब चोर चोरी कइल सामान छोड़के भागे लागल, त नींद से जागला के बाद पौहारी बाबा ओकरा पाछे दौड़लें आ उल्टे ऊ सामान चोर के देवे के कोशिश कइलें। एह घटना के चोर पर एतना गहिर असर पड़ल कि बाद में ऊ संन्यासी बन गइल आ पौहारी बाबा के अनुयायी हो गइल।
1890 में [[स्वामी विवेकानंद]] गाजीपुर गइलें आ पौहारी बाबा से भेंट कइलें। [[सिस्टर निवेदिता]] के अनुसार, 1898 में पौहारी बाबा आग में जलके देह त्याग दिहलें। एह घटना के सामान्य रूप से आत्मदाह के रूप में देखल जाला।<ref name="Nikhilananda p44—45" />
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
== स्रोत ==
* {{cite book|last=Nikhilananda|first=Swami|title=Vivekânanda: a biography|url=http://www.vivekananda.net/PDFBooks/BiographybyNikhilananda.pdf|year=1953|publisher=Ramakrishna-Vivekananda Center|isbn=978-0-911206-25-8}}
[[श्रेणी:भारतीय हिंदू संत]]
[[श्रेणी:भारतीय हिंदू योगी]]
{{Bio-stub}}
ofrlg1e4mjnomvlre1u0yvazcf8zq4t
803391
803390
2026-07-29T18:12:32Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा [[श्रेणी:गाजीपुर के लोग]] जोड़ल गइल
803391
wikitext
text/x-wiki
{{Infobox Hindu leader
|religion = [[हिंदू धर्म]]
|birth_date = 1798
|birth_place = [[जौनपुर जिला|जौनपुर]]
|death_date = 1898
|death_place = [[गाजीपुर]] (उमिर 99-100)
|death_cause = दाह (आत्म-दाह)<ref name="Nikhilananda p44—45" />
|philosophy = [[हठयोग]]
|influenced = [[स्वामी विवेकानंद]]
}}
'''पौहारी बाबा''' (1798–1898), जिनका के '''पवहारी''' नाँव से भी जानल जाला, एगो हिंदू संन्यासी आ संत रहलें। उनकर जनम [[जौनपुर जिला|जौनपुर]] के प्रेमापुर गाँव में एगो ब्राह्मण परिवार में भइल रहे। बचपन में ऊ अपना चाचा के देखरेख में पढ़ाई करे खातिर [[गाजीपुर]] गइलें। उनकर चाचा [[रामानुज]] भा श्री संप्रदाय के अनुयायी रहलें। पढ़ाई पूरा कइला के बाद पौहारी बाबा कई गो जगह के यात्रा कइलें। [[काठियावाड़]] के [[गिरनार]] में उनकरा के [[योग]] के दीक्षा मिलल।<ref name="Nikhilananda p44—45">{{Harvnb|Nikhilananda|1953|pp=44–45}}</ref>
ओकरा बाद ऊ गाजीपुर लवट अइलें आ अपना घरे में जमीन के नीचे एगो साधना-स्थल (गुफा जइसन आश्रम) बनवलें, जहाँ ऊ कई-कई दिन तक [[ध्यान]] आ योग के साधना करत रहलें। ऊ अपना नम्रता, विनम्र व्यवहार आ परोपकार के भावना खातिर जानल जात रहलें। कथा हवे कि, एक रात एगो चोर उनकर आश्रम में घुस गइल। जब चोर चोरी कइल सामान छोड़के भागे लागल, त नींद से जागला के बाद पौहारी बाबा ओकरा पाछे दौड़लें आ उल्टे ऊ सामान चोर के देवे के कोशिश कइलें। एह घटना के चोर पर एतना गहिर असर पड़ल कि बाद में ऊ संन्यासी बन गइल आ पौहारी बाबा के अनुयायी हो गइल।
1890 में [[स्वामी विवेकानंद]] गाजीपुर गइलें आ पौहारी बाबा से भेंट कइलें। [[सिस्टर निवेदिता]] के अनुसार, 1898 में पौहारी बाबा आग में जलके देह त्याग दिहलें। एह घटना के सामान्य रूप से आत्मदाह के रूप में देखल जाला।<ref name="Nikhilananda p44—45" />
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
== स्रोत ==
* {{cite book|last=Nikhilananda|first=Swami|title=Vivekânanda: a biography|url=http://www.vivekananda.net/PDFBooks/BiographybyNikhilananda.pdf|year=1953|publisher=Ramakrishna-Vivekananda Center|isbn=978-0-911206-25-8}}
[[श्रेणी:भारतीय हिंदू संत]]
[[श्रेणी:भारतीय हिंदू योगी]]
[[श्रेणी:गाजीपुर के लोग]]
{{Bio-stub}}
rv3i79edg5arjtxtdvitwzgb59wrsvg
दयानंद सरस्वती
0
101347
803393
2026-07-29T18:43:23Z
SM7
3953
पन्ना बनावल गइल "'''दयानंद सरस्वती''' (12 फरवरी 1824 – 30 अक्टूबर 1883), जिनकर जनम मूलशंकर तिवारी के रूप में भइल रहे, एगो हिंदू दार्शनिक, समाज सुधारक आ [[आर्य समाज]] के संस्थापक रहलें। आर्य समाज हिंदू..." के साथ
803393
wikitext
text/x-wiki
'''दयानंद सरस्वती''' (12 फरवरी 1824 – 30 अक्टूबर 1883), जिनकर जनम मूलशंकर तिवारी के रूप में भइल रहे, एगो हिंदू दार्शनिक, समाज सुधारक आ [[आर्य समाज]] के संस्थापक रहलें। आर्य समाज हिंदू धर्म के एगो सुधारवादी आंदोलन ह। उनकर लिखल किताब [[सत्यार्थ प्रकाश]] [[वेद]] के दर्शन आ मनुष्य के अलग-अलग विचार आ कर्तव्य के व्याख्या करे वाला सबसे प्रभावशाली ग्रंथ सभ में से एगो मानल जाला।
1876 में दयानंद सरस्वती सबसे पहिले "भारत भारतीयन खातिर" के नारा देत [[स्वराज]] के आह्वान कइलें। बाद में एह नारा के [[लोकमान्य तिलक]] भी अपनवलें। मूर्ति-पूजा आ कर्मकांड के विरोध करत ऊ वैदिक धर्म के पुनर्जीवित करे के दिशा में काम कइलें।
बाद में भारत के दार्शनिक आ राष्ट्रपति [[सर्वपल्ली राधाकृष्णन]] दयानंद सरस्वती के "आधुनिक भारत के निर्माता" सभ में से एगो कहलें। [[श्री अरविंद]] भी उनकरा बारे में एह तरह के सम्मानजनक विचार व्यक्त कइलें।
{{clear}}
{{जीवनी-आधार}}
e39de7xd6zh9k9mxul3tokvorfos1yh
803395
803393
2026-07-29T18:48:10Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा [[श्रेणी:भारतीय हिंदू धार्मिक नेता]] जोड़ल गइल
803395
wikitext
text/x-wiki
'''दयानंद सरस्वती''' (12 फरवरी 1824 – 30 अक्टूबर 1883), जिनकर जनम मूलशंकर तिवारी के रूप में भइल रहे, एगो हिंदू दार्शनिक, समाज सुधारक आ [[आर्य समाज]] के संस्थापक रहलें। आर्य समाज हिंदू धर्म के एगो सुधारवादी आंदोलन ह। उनकर लिखल किताब [[सत्यार्थ प्रकाश]] [[वेद]] के दर्शन आ मनुष्य के अलग-अलग विचार आ कर्तव्य के व्याख्या करे वाला सबसे प्रभावशाली ग्रंथ सभ में से एगो मानल जाला।
1876 में दयानंद सरस्वती सबसे पहिले "भारत भारतीयन खातिर" के नारा देत [[स्वराज]] के आह्वान कइलें। बाद में एह नारा के [[लोकमान्य तिलक]] भी अपनवलें। मूर्ति-पूजा आ कर्मकांड के विरोध करत ऊ वैदिक धर्म के पुनर्जीवित करे के दिशा में काम कइलें।
बाद में भारत के दार्शनिक आ राष्ट्रपति [[सर्वपल्ली राधाकृष्णन]] दयानंद सरस्वती के "आधुनिक भारत के निर्माता" सभ में से एगो कहलें। [[श्री अरविंद]] भी उनकरा बारे में एह तरह के सम्मानजनक विचार व्यक्त कइलें।
{{clear}}
[[श्रेणी:भारतीय हिंदू धार्मिक नेता]]
{{जीवनी-आधार}}
ek3gy5kjrfmarq4njhyq0yf4fhm7zsg
विमियो
0
101348
803397
2026-07-29T23:21:53Z
SM7
3953
नया आधार लेख / अंग्रेजी विकिपीडिया से अनुबाद कइ के
803397
wikitext
text/x-wiki
'''विमियो''' एगो अमेरिकी [[ऑनलाइन बीडियो प्लेटफॉर्म|बीडियो होस्टिंग]], बीडियो साझा करे आ [[बीडियो]] से जुड़ल सेवा देवे वाला प्लेटफार्म ह। एकर स्थापना 2004 में भइल आ एकर मुख्यालय [[न्यूयॉर्क सिटी]] में बा। विमियो के खास ध्यान अलग-अलग तरह के डिवाइसन पर हाई-डेफिनिशन (ऊँच-क्वालिटी वाला) बीडियो उपलब्ध करावे पर बा। ई ''सॉफ्टवेयर ऐज़ अ सर्विस'' (SaaS) बिजनेस मॉडल पर काम करेला। एह प्लेटफॉर्म पर बीडियो बनावे, एडिट करे आ लाइव ब्रॉडकास्ट करे के औजार उपलब्ध बाड़ें। एकरा अलावा ई एंटरप्राइजन खातिर सॉफ्टवेयर सोल्यूशनो देला आ बीडियो प्रोफेशनल लोग के अपना ग्राहक आ दूसर पेशेवर लोग से जुड़े के सुविधा भी उपलब्ध करावेला। दिसंबर 2021 तक एह मंच पर लगभग 26 करोड़ उपयोगकर्ता रहलें, जवना में से करीब 16 लाख लोग एकर पेमेंट वाला सर्विस के सदस्य रहल।
विमियो के शुरुआत 2004 में [[जेक लोडविक]] आ [[ज़ैक क्लेन]] कॉलेजह्यूमर के एगो एक्सपेरिमेंटल प्रोजेक्ट के रूप में कइलें। एह प्रोजेक्ट के मोटिवेशन ओही साल शुरू भइल फोटो साझा करे वाला मंच [[फ़्लिकर]] से मिलल रहे। बाद में एकरा के कॉलेजह्यूमर के मूल कंपनी कनेक्टेड वेंचर्स के एगो डिपार्टमेंट के रूप में संगठित कइल गइल। एह स्टार्टअप के स्थापना रिकी वैन वीन, जोश अब्रामसन, जेक लोडविक आ ज़ैक क्लेन मिलके कइल लोग। अगस्त 2006 में आईएसी (IAC) 2.1 करोड़ अमेरिकी डॉलर में कनेक्टेड वेंचर्स में 51 परसेंट कंट्रोलिंग हिस्सेदारी खरीद लिहलस। एकरा से तीनो महीना से कम समय बाद [[गूगल]], [[यूट्यूब]] के, 165 करोड़ अमेरिकी डॉलर से बेसी रकम में खरीद लिहलस। एह अधिग्रहण के बाद आईएसी विमियो के विकास पर अउरी बेसी ध्यान देवे लागल। 2008 तक जेक लोडविक आ ज़ैक क्लेन कंपनी छोड़ दिहलें, आ आईएसी विमियो के सेवा के विस्तार करे खातिर एकरा के अउरी पेशेवर कॉर्पोरेट ढाँचा दे दिहलस। मई 2021 में विमियो एगो स्वतंत्र पब्लिक कंपनी बन गइल। हालाँकि, सितंबर 2025 में बेंडिंग स्पून्स 138 करोड़ अमेरिकी डॉलर के पूरा नकद सौदा के तहत विमियो के निजी कंपनी बनावे के घोषणा कइलस। ई सौदा नवंबर 2025 में पूरा भइल।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:अमेरिकी कंपनी]]
{{कंपनी-आधार}}
c3v2c1jcn0gh3jru6y5zmxi045d4ha2
803458
803397
2026-07-29T23:50:00Z
SM7
3953
सुधार कइल गइल
803458
wikitext
text/x-wiki
{{Infobox company
| name = वीमियो, इंक.
| trade_name = वीमियो
| logo = Vimeo Logo.svg
| founder = {{ubl|[[जेक लॉडविक]] ([[मुख्य प्रौद्योगिकी अधिकारी|सह-संस्थापक]])|[[जैक क्लेन]] ([[मुख्य डिजाइन अधिकारी|सह-संस्थापक]])|[[जोश अब्रामसन]] ([[मुख्य वित्तीय अधिकारी|भागीदार]])|[[रिकी वैन वीन]] ([[मुख्य परिचालन अधिकारी|भागीदार]])}}
| type = [[सहायक कंपनी]]
| traded_as = {{ubl|class=nowrap|{{NASDAQ was|VMEO}}}} (2021–2025)
| founded = {{Launch date and age|2004|11|14}}
| area_served = दुनिया भर में ([[चीन]], [[इंडोनेशिया]], [[उत्तर कोरिया]] आ [[तुर्की]] के छोड़के)
| subsidiaries = {{ubl|[[वीएचएक्स]]|[[वीमियो लाइवस्ट्रीम]]|[[मैजिस्टो]]}}
| hq_location_city = [[न्यूयॉर्क सिटी]]
| hq_location_country = संयुक्त राज्य अमेरिका
| key_people = {{ubl|फिलिप मोयर ([[मुख्य कार्यकारी अधिकारी|सीईओ]])}}
| num_employees = 1,102 (दिसंबर 2024)
| parent = {{ubl|[[आईएसी]] (2006–2021)|[[बेंडिंग स्पून्स]] (2025–अबले)|}}
| products = {{ubl|[[वीएचएक्स|वीमियो ओटीटी]]|[[वीमियो लाइवस्ट्रीम]]|वीमियो स्टॉक|वीमियो एंटरप्राइज|वीमियो क्रिएट|वीमियो रिकॉर्ड}}
| revenue = {{Unchanged}} {{US$|417 million|link=yes}} (2024)
| operating_income = {{Increase}} {{US$|19 million}} (2024)
| net_income = {{Increase}} {{US$|27 million}} (2024)
| assets = {{Increase}} {{US$|643 million}} (2024)
| equity = {{Increase}} {{US$|409 million}} (2024)
| website = {{URL|https://vimeo.com}}
| module = {{Infobox website
| embed = yes
| name = <wbr />​<strong>Vimeo.com</strong>
| current_status = सक्रिय
| num_users = 300 मिलियन (2023)
| website_type = [[वीडियो होस्टिंग सेवा]], [[सॉफ्टवेयर एगो सेवा के रूप में]]
| launch_date = {{start date and age|2004|12|15}}
| advertising = ना
| registration = वैकल्पिक
| language = 7 भाषा: अंग्रेजी, स्पेनी, जर्मन, फ्रेंच, जापानी, पुर्तगाली, कोरियाई
}}
}}
'''विमियो''' एगो अमेरिकी [[ऑनलाइन बीडियो प्लेटफॉर्म|बीडियो होस्टिंग]], बीडियो साझा करे आ [[बीडियो]] से जुड़ल सेवा देवे वाला प्लेटफार्म ह। एकर स्थापना 2004 में भइल आ एकर मुख्यालय [[न्यूयॉर्क सिटी]] में बा। विमियो के खास ध्यान अलग-अलग तरह के डिवाइसन पर हाई-डेफिनिशन (ऊँच-क्वालिटी वाला) बीडियो उपलब्ध करावे पर बा। ई ''सॉफ्टवेयर ऐज़ अ सर्विस'' (SaaS) बिजनेस मॉडल पर काम करेला। एह प्लेटफॉर्म पर बीडियो बनावे, एडिट करे आ लाइव ब्रॉडकास्ट करे के औजार उपलब्ध बाड़ें। एकरा अलावा ई एंटरप्राइजन खातिर सॉफ्टवेयर सोल्यूशनो देला आ बीडियो प्रोफेशनल लोग के अपना ग्राहक आ दूसर पेशेवर लोग से जुड़े के सुविधा भी उपलब्ध करावेला। दिसंबर 2021 तक एह मंच पर लगभग 26 करोड़ उपयोगकर्ता रहलें, जवना में से करीब 16 लाख लोग एकर पेमेंट वाला सर्विस के सदस्य रहल।
विमियो के शुरुआत 2004 में [[जेक लोडविक]] आ [[ज़ैक क्लेन]] कॉलेजह्यूमर के एगो एक्सपेरिमेंटल प्रोजेक्ट के रूप में कइलें। एह प्रोजेक्ट के मोटिवेशन ओही साल शुरू भइल फोटो साझा करे वाला मंच [[फ़्लिकर]] से मिलल रहे। बाद में एकरा के कॉलेजह्यूमर के मूल कंपनी कनेक्टेड वेंचर्स के एगो डिपार्टमेंट के रूप में संगठित कइल गइल। एह स्टार्टअप के स्थापना रिकी वैन वीन, जोश अब्रामसन, जेक लोडविक आ ज़ैक क्लेन मिलके कइल लोग। अगस्त 2006 में आईएसी (IAC) 2.1 करोड़ अमेरिकी डॉलर में कनेक्टेड वेंचर्स में 51 परसेंट कंट्रोलिंग हिस्सेदारी खरीद लिहलस। एकरा से तीनो महीना से कम समय बाद [[गूगल]], [[यूट्यूब]] के, 165 करोड़ अमेरिकी डॉलर से बेसी रकम में खरीद लिहलस। एह अधिग्रहण के बाद आईएसी विमियो के विकास पर अउरी बेसी ध्यान देवे लागल। 2008 तक जेक लोडविक आ ज़ैक क्लेन कंपनी छोड़ दिहलें, आ आईएसी विमियो के सेवा के विस्तार करे खातिर एकरा के अउरी पेशेवर कॉर्पोरेट ढाँचा दे दिहलस। मई 2021 में विमियो एगो स्वतंत्र पब्लिक कंपनी बन गइल। हालाँकि, सितंबर 2025 में बेंडिंग स्पून्स 138 करोड़ अमेरिकी डॉलर के पूरा नकद सौदा के तहत विमियो के निजी कंपनी बनावे के घोषणा कइलस। ई सौदा नवंबर 2025 में पूरा भइल।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:अमेरिकी कंपनी]]
{{कंपनी-आधार}}
gr0906uhpulkwvia8t9esb3ps06xa89
803476
803458
2026-07-30T00:05:24Z
SM7
3953
सुधार कइल गइल
803476
wikitext
text/x-wiki
<!-- {{Infobox company
| name = वीमियो, इंक.
| trade_name = वीमियो
| logo = Vimeo Logo.svg
| founder = {{ubl|[[जेक लॉडविक]] ([[मुख्य प्रौद्योगिकी अधिकारी|सह-संस्थापक]])|[[जैक क्लेन]] ([[मुख्य डिजाइन अधिकारी|सह-संस्थापक]])|[[जोश अब्रामसन]] ([[मुख्य वित्तीय अधिकारी|भागीदार]])|[[रिकी वैन वीन]] ([[मुख्य परिचालन अधिकारी|भागीदार]])}}
| type = [[सहायक कंपनी]]
| traded_as = {{ubl|class=nowrap|{{NASDAQ was|VMEO}}}} (2021–2025)
| founded = {{Launch date and age|2004|11|14}}
| area_served = दुनिया भर में ([[चीन]], [[इंडोनेशिया]], [[उत्तर कोरिया]] आ [[तुर्की]] के छोड़के)
| subsidiaries = {{ubl|[[वीएचएक्स]]|[[वीमियो लाइवस्ट्रीम]]|[[मैजिस्टो]]}}
| hq_location_city = [[न्यूयॉर्क सिटी]]
| hq_location_country = संयुक्त राज्य अमेरिका
| key_people = {{ubl|फिलिप मोयर ([[मुख्य कार्यकारी अधिकारी|सीईओ]])}}
| num_employees = 1,102 (दिसंबर 2024)
| parent = {{ubl|[[आईएसी]] (2006–2021)|[[बेंडिंग स्पून्स]] (2025–अबले)|}}
| products = {{ubl|[[वीएचएक्स|वीमियो ओटीटी]]|[[वीमियो लाइवस्ट्रीम]]|वीमियो स्टॉक|वीमियो एंटरप्राइज|वीमियो क्रिएट|वीमियो रिकॉर्ड}}
| revenue = {{Unchanged}} {{US$|417 million|link=yes}} (2024)
| operating_income = {{Increase}} {{US$|19 million}} (2024)
| net_income = {{Increase}} {{US$|27 million}} (2024)
| assets = {{Increase}} {{US$|643 million}} (2024)
| equity = {{Increase}} {{US$|409 million}} (2024)
| website = {{URL|https://vimeo.com}}
| module = {{Infobox website
| embed = yes
| name = <wbr />​<strong>Vimeo.com</strong>
| current_status = सक्रिय
| num_users = 300 मिलियन (2023)
| website_type = [[वीडियो होस्टिंग सेवा]], [[सॉफ्टवेयर एगो सेवा के रूप में]]
| launch_date = {{start date and age|2004|12|15}}
| advertising = ना
| registration = वैकल्पिक
| language = 7 भाषा: अंग्रेजी, स्पेनी, जर्मन, फ्रेंच, जापानी, पुर्तगाली, कोरियाई
}}
| footnotes = <ref name=AR24>{{cite web |title=Vimeo, Inc. 2024 Annual Report (Form 10-K) |url=https://www.sec.gov/Archives/edgar/data/1837686/000183768625000024/vmeo-20241231.htm |website=SEC.gov |publisher=[[U.S. Securities and Exchange Commission]] |date=February 19, 2025 |access-date=March 13, 2025}}</ref><ref name=AR>{{cite web |title=Vimeo, Inc. 2023 Annual Report (Form 10-K) |url=https://www.sec.gov/ix?doc=/Archives/edgar/data/1837686/000183768624000021/vmeo-20231231.htm |website=SEC.gov |publisher=[[U.S. Securities and Exchange Commission]] |date=27 February 2023 |access-date=May 21, 2024 |archive-date=May 21, 2024 |archive-url=https://web.archive.org/web/20240521023149/https://www.sec.gov/ix?doc=/Archives/edgar/data/1837686/000183768624000021/vmeo-20231231.htm |url-status=live }}</ref><ref name=Prototype>{{cite web |title=Vimeo on the Internet Archive |url=http://www.blumpy.org/vimeo/|archive-url=https://web.archive.org/web/20041217132740/http://www.blumpy.org/vimeo/ |archive-date=December 17, 2004}}</ref><ref name=domain>{{cite web|title=Whois Record for <code>vimeo.com</code> |url=https://whois.domaintools.com/vimeo.com |publisher=DomainTools |access-date=May 1, 2025}}</ref>
}}-->
'''विमियो''' ({{Langx|en|vimeo}}; {{IPAc-en|ˈ|v|ɪ|m|i|oʊ|}} {{respell|VIM|ee|oh}})<ref>{{cite web|url=https://vimeo.com/79338405|title=How to pronounce Vimeo?|publisher=Vimeo|access-date=January 17, 2019|archive-date=May 30, 2021|archive-url=https://web.archive.org/web/20210530224517/https://vimeo.com/79338405|url-status=live}}</ref> एगो अमेरिकी [[ऑनलाइन बीडियो प्लेटफॉर्म|बीडियो होस्टिंग]], बीडियो साझा करे आ [[बीडियो]] से जुड़ल सेवा देवे वाला प्लेटफार्म ह। एकर स्थापना 2004 में भइल आ एकर मुख्यालय [[न्यूयॉर्क सिटी]] में बा। विमियो के खास ध्यान अलग-अलग तरह के डिवाइसन पर हाई-डेफिनिशन (ऊँच-क्वालिटी वाला) बीडियो उपलब्ध करावे पर बा। ई ''सॉफ्टवेयर ऐज़ अ सर्विस'' (SaaS) बिजनेस मॉडल पर काम करेला। एह प्लेटफॉर्म पर बीडियो बनावे, एडिट करे आ लाइव ब्रॉडकास्ट करे के औजार उपलब्ध बाड़ें। एकरा अलावा ई एंटरप्राइजन खातिर सॉफ्टवेयर सोल्यूशनो देला आ बीडियो प्रोफेशनल लोग के अपना ग्राहक आ दूसर पेशेवर लोग से जुड़े के सुविधा भी उपलब्ध करावेला। दिसंबर 2021 तक एह मंच पर लगभग 26 करोड़ उपयोगकर्ता रहलें, जवना में से करीब 16 लाख लोग एकर पेमेंट वाला सर्विस के सदस्य रहल।
विमियो के शुरुआत 2004 में [[जेक लोडविक]] आ [[ज़ैक क्लेन]] कॉलेजह्यूमर के एगो एक्सपेरिमेंटल प्रोजेक्ट के रूप में कइलें। एह प्रोजेक्ट के मोटिवेशन ओही साल शुरू भइल फोटो साझा करे वाला मंच [[फ़्लिकर]] से मिलल रहे। बाद में एकरा के कॉलेजह्यूमर के मूल कंपनी कनेक्टेड वेंचर्स के एगो डिपार्टमेंट के रूप में संगठित कइल गइल। एह स्टार्टअप के स्थापना रिकी वैन वीन, जोश अब्रामसन, जेक लोडविक आ ज़ैक क्लेन मिलके कइल लोग। अगस्त 2006 में आईएसी (IAC) 2.1 करोड़ अमेरिकी डॉलर में कनेक्टेड वेंचर्स में 51 परसेंट कंट्रोलिंग हिस्सेदारी खरीद लिहलस। एकरा से तीनो महीना से कम समय बाद [[गूगल]], [[यूट्यूब]] के, 165 करोड़ अमेरिकी डॉलर से बेसी रकम में खरीद लिहलस। एह अधिग्रहण के बाद आईएसी विमियो के विकास पर अउरी बेसी ध्यान देवे लागल। 2008 तक जेक लोडविक आ ज़ैक क्लेन कंपनी छोड़ दिहलें, आ आईएसी विमियो के सेवा के विस्तार करे खातिर एकरा के अउरी पेशेवर कॉर्पोरेट ढाँचा दे दिहलस। मई 2021 में विमियो एगो स्वतंत्र पब्लिक कंपनी बन गइल। हालाँकि, सितंबर 2025 में बेंडिंग स्पून्स 138 करोड़ अमेरिकी डॉलर के पूरा नकद सौदा के तहत विमियो के निजी कंपनी बनावे के घोषणा कइलस। ई सौदा नवंबर 2025 में पूरा भइल।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:अमेरिकी कंपनी]]
{{कंपनी-आधार}}
h6it9hcqhqk05gk00nk8ygwl0rystqz
803477
803476
2026-07-30T00:05:46Z
SM7
3953
[[विकिपीडिया:हॉट-कैट|हॉट-कैट]] द्वारा [[श्रेणी:बीडियो होस्टिंग]] जोड़ल गइल
803477
wikitext
text/x-wiki
<!-- {{Infobox company
| name = वीमियो, इंक.
| trade_name = वीमियो
| logo = Vimeo Logo.svg
| founder = {{ubl|[[जेक लॉडविक]] ([[मुख्य प्रौद्योगिकी अधिकारी|सह-संस्थापक]])|[[जैक क्लेन]] ([[मुख्य डिजाइन अधिकारी|सह-संस्थापक]])|[[जोश अब्रामसन]] ([[मुख्य वित्तीय अधिकारी|भागीदार]])|[[रिकी वैन वीन]] ([[मुख्य परिचालन अधिकारी|भागीदार]])}}
| type = [[सहायक कंपनी]]
| traded_as = {{ubl|class=nowrap|{{NASDAQ was|VMEO}}}} (2021–2025)
| founded = {{Launch date and age|2004|11|14}}
| area_served = दुनिया भर में ([[चीन]], [[इंडोनेशिया]], [[उत्तर कोरिया]] आ [[तुर्की]] के छोड़के)
| subsidiaries = {{ubl|[[वीएचएक्स]]|[[वीमियो लाइवस्ट्रीम]]|[[मैजिस्टो]]}}
| hq_location_city = [[न्यूयॉर्क सिटी]]
| hq_location_country = संयुक्त राज्य अमेरिका
| key_people = {{ubl|फिलिप मोयर ([[मुख्य कार्यकारी अधिकारी|सीईओ]])}}
| num_employees = 1,102 (दिसंबर 2024)
| parent = {{ubl|[[आईएसी]] (2006–2021)|[[बेंडिंग स्पून्स]] (2025–अबले)|}}
| products = {{ubl|[[वीएचएक्स|वीमियो ओटीटी]]|[[वीमियो लाइवस्ट्रीम]]|वीमियो स्टॉक|वीमियो एंटरप्राइज|वीमियो क्रिएट|वीमियो रिकॉर्ड}}
| revenue = {{Unchanged}} {{US$|417 million|link=yes}} (2024)
| operating_income = {{Increase}} {{US$|19 million}} (2024)
| net_income = {{Increase}} {{US$|27 million}} (2024)
| assets = {{Increase}} {{US$|643 million}} (2024)
| equity = {{Increase}} {{US$|409 million}} (2024)
| website = {{URL|https://vimeo.com}}
| module = {{Infobox website
| embed = yes
| name = <wbr />​<strong>Vimeo.com</strong>
| current_status = सक्रिय
| num_users = 300 मिलियन (2023)
| website_type = [[वीडियो होस्टिंग सेवा]], [[सॉफ्टवेयर एगो सेवा के रूप में]]
| launch_date = {{start date and age|2004|12|15}}
| advertising = ना
| registration = वैकल्पिक
| language = 7 भाषा: अंग्रेजी, स्पेनी, जर्मन, फ्रेंच, जापानी, पुर्तगाली, कोरियाई
}}
| footnotes = <ref name=AR24>{{cite web |title=Vimeo, Inc. 2024 Annual Report (Form 10-K) |url=https://www.sec.gov/Archives/edgar/data/1837686/000183768625000024/vmeo-20241231.htm |website=SEC.gov |publisher=[[U.S. Securities and Exchange Commission]] |date=February 19, 2025 |access-date=March 13, 2025}}</ref><ref name=AR>{{cite web |title=Vimeo, Inc. 2023 Annual Report (Form 10-K) |url=https://www.sec.gov/ix?doc=/Archives/edgar/data/1837686/000183768624000021/vmeo-20231231.htm |website=SEC.gov |publisher=[[U.S. Securities and Exchange Commission]] |date=27 February 2023 |access-date=May 21, 2024 |archive-date=May 21, 2024 |archive-url=https://web.archive.org/web/20240521023149/https://www.sec.gov/ix?doc=/Archives/edgar/data/1837686/000183768624000021/vmeo-20231231.htm |url-status=live }}</ref><ref name=Prototype>{{cite web |title=Vimeo on the Internet Archive |url=http://www.blumpy.org/vimeo/|archive-url=https://web.archive.org/web/20041217132740/http://www.blumpy.org/vimeo/ |archive-date=December 17, 2004}}</ref><ref name=domain>{{cite web|title=Whois Record for <code>vimeo.com</code> |url=https://whois.domaintools.com/vimeo.com |publisher=DomainTools |access-date=May 1, 2025}}</ref>
}}-->
'''विमियो''' ({{Langx|en|vimeo}}; {{IPAc-en|ˈ|v|ɪ|m|i|oʊ|}} {{respell|VIM|ee|oh}})<ref>{{cite web|url=https://vimeo.com/79338405|title=How to pronounce Vimeo?|publisher=Vimeo|access-date=January 17, 2019|archive-date=May 30, 2021|archive-url=https://web.archive.org/web/20210530224517/https://vimeo.com/79338405|url-status=live}}</ref> एगो अमेरिकी [[ऑनलाइन बीडियो प्लेटफॉर्म|बीडियो होस्टिंग]], बीडियो साझा करे आ [[बीडियो]] से जुड़ल सेवा देवे वाला प्लेटफार्म ह। एकर स्थापना 2004 में भइल आ एकर मुख्यालय [[न्यूयॉर्क सिटी]] में बा। विमियो के खास ध्यान अलग-अलग तरह के डिवाइसन पर हाई-डेफिनिशन (ऊँच-क्वालिटी वाला) बीडियो उपलब्ध करावे पर बा। ई ''सॉफ्टवेयर ऐज़ अ सर्विस'' (SaaS) बिजनेस मॉडल पर काम करेला। एह प्लेटफॉर्म पर बीडियो बनावे, एडिट करे आ लाइव ब्रॉडकास्ट करे के औजार उपलब्ध बाड़ें। एकरा अलावा ई एंटरप्राइजन खातिर सॉफ्टवेयर सोल्यूशनो देला आ बीडियो प्रोफेशनल लोग के अपना ग्राहक आ दूसर पेशेवर लोग से जुड़े के सुविधा भी उपलब्ध करावेला। दिसंबर 2021 तक एह मंच पर लगभग 26 करोड़ उपयोगकर्ता रहलें, जवना में से करीब 16 लाख लोग एकर पेमेंट वाला सर्विस के सदस्य रहल।
विमियो के शुरुआत 2004 में [[जेक लोडविक]] आ [[ज़ैक क्लेन]] कॉलेजह्यूमर के एगो एक्सपेरिमेंटल प्रोजेक्ट के रूप में कइलें। एह प्रोजेक्ट के मोटिवेशन ओही साल शुरू भइल फोटो साझा करे वाला मंच [[फ़्लिकर]] से मिलल रहे। बाद में एकरा के कॉलेजह्यूमर के मूल कंपनी कनेक्टेड वेंचर्स के एगो डिपार्टमेंट के रूप में संगठित कइल गइल। एह स्टार्टअप के स्थापना रिकी वैन वीन, जोश अब्रामसन, जेक लोडविक आ ज़ैक क्लेन मिलके कइल लोग। अगस्त 2006 में आईएसी (IAC) 2.1 करोड़ अमेरिकी डॉलर में कनेक्टेड वेंचर्स में 51 परसेंट कंट्रोलिंग हिस्सेदारी खरीद लिहलस। एकरा से तीनो महीना से कम समय बाद [[गूगल]], [[यूट्यूब]] के, 165 करोड़ अमेरिकी डॉलर से बेसी रकम में खरीद लिहलस। एह अधिग्रहण के बाद आईएसी विमियो के विकास पर अउरी बेसी ध्यान देवे लागल। 2008 तक जेक लोडविक आ ज़ैक क्लेन कंपनी छोड़ दिहलें, आ आईएसी विमियो के सेवा के विस्तार करे खातिर एकरा के अउरी पेशेवर कॉर्पोरेट ढाँचा दे दिहलस। मई 2021 में विमियो एगो स्वतंत्र पब्लिक कंपनी बन गइल। हालाँकि, सितंबर 2025 में बेंडिंग स्पून्स 138 करोड़ अमेरिकी डॉलर के पूरा नकद सौदा के तहत विमियो के निजी कंपनी बनावे के घोषणा कइलस। ई सौदा नवंबर 2025 में पूरा भइल।
{{clear}}
== संदर्भ ==
{{Reflist|29em}}
[[श्रेणी:अमेरिकी कंपनी]]
[[श्रेणी:बीडियो होस्टिंग]]
{{कंपनी-आधार}}
q36g9gxwlbmrop4lppgub0cxm4bid67
Module:I18n/doc
828
101349
803398
2024-11-20T01:00:27Z
en>Awesome Aasim
0
803398
wikitext
text/x-wiki
{{Lua autodoc|file=I18n|autodoc=yes}}
<noinclude>
[[Category:Module documentation pages]]
</noinclude>
ifmao5vx2uxg6hwm20cvnusihf83hmg
803399
803398
2026-07-29T23:40:19Z
SM7
3953
1 revision imported from [[:en:Module:I18n/doc]]
803398
wikitext
text/x-wiki
{{Lua autodoc|file=I18n|autodoc=yes}}
<noinclude>
[[Category:Module documentation pages]]
</noinclude>
ifmao5vx2uxg6hwm20cvnusihf83hmg
टेम्पलेट:Lua autodoc
10
101350
803400
2024-09-26T02:22:47Z
en>Awesome Aasim
0
[[WP:AES|←]]Created page with '<includeonly>{{#invoke:Docbunto|main}}</includeonly><noinclude> {{documentation}} </noinclude>'
803400
wikitext
text/x-wiki
<includeonly>{{#invoke:Docbunto|main}}</includeonly><noinclude>
{{documentation}}
</noinclude>
io4wp1dpjy0cnp37az2fv47kkj2k6hm
803401
803400
2026-07-29T23:40:40Z
SM7
3953
1 revision imported from [[:en:Template:Lua_autodoc]]
803400
wikitext
text/x-wiki
<includeonly>{{#invoke:Docbunto|main}}</includeonly><noinclude>
{{documentation}}
</noinclude>
io4wp1dpjy0cnp37az2fv47kkj2k6hm
Module:Docbunto
828
101351
803402
2026-06-25T18:24:51Z
en>Awesome Aasim
0
803402
Scribunto
text/plain
--- Docbunto is an automatic documentation generator for Scribunto modules.
-- The module is based on LuaDoc and LDoc. It produces documentation in
-- the form of MediaWiki markup, using `@tag`-prefixed comments embedded
-- in the source code of a Scribunto module. The taglet parser & doclet
-- renderer Docbunto uses are also publicly exposed to other modules.
--
-- Docbunto code items are introduced by a block comment (`--[[]]--`), an
-- inline comment with three hyphens (`---`), or an inline `@tag` comment.
-- The module can use static code analysis to infer variable names, item
-- privacy (`local` keyword), tables (`{}` constructor) and functions
-- (`function` keyword). MediaWiki and Markdown formatting is supported.
--
-- Items are usually rendered in the order they are defined, if they are
-- public items, or emulated classes extending the Lua primitives. There
-- are many customisation options available to change Docbunto behaviour.
--
-- @module docbunto
-- @alias p
-- @require Module:I18n
-- @require Module:Lua_lexer
-- @require Module:Unindent
-- @require Module:Yesno
-- @require Module:Arguments
-- @require Module:Documentation
-- @author [[wikia:dev:User:8nml|8nml]] (Fandom Dev Wiki)
-- @author [[User:Awesome Aasim|Awesome Aasim]] (Wikimedia port)
-- @attribution [https://github.com/stevedonovan @stevedonovan] ([https://github.com/stevedonovan/LDoc GitHub])
-- @release beta
-- <nowiki>
local p = {}
-- Module dependencies.
local title = mw.title.getCurrentTitle()
local i18n = require('Module:I18n').loadMessages('Docbunto'):useContentLang()
local references = mw.loadData('Module:Docbunto/references')
local lexer = require('Module:Lua lexer')
local unindent = require('Module:Unindent')
local yesno = require('Module:Yesno')
local doc = require('Module:Documentation')
local fileTooBig
local usingOptionsContent
local hasTodoItems
-- Module variables.
local DEFAULT_TITLE = title.namespace == 828 and doc.getEnvironment({}).templateTitle.text or ''
local DEFAULT_VARIABLE = DEFAULT_TITLE
:gsub('^%u', mw.ustring.lower)
:gsub('%s', '_'):gsub('%p', '.')
local frame, gsub, match
local syntaxhighlightPresent = mw.getCurrentFrame()
:preprocess("<syntaxhighlight lang=\"lua\">-- Lua</syntaxhighlight>")
~= "<syntaxhighlight lang=\"lua\">-- Lua</syntaxhighlight>"
-- Docbunto variables & tag tokens.
local TAG_MULTI = 'M'
local TAG_ID = 'ID'
local TAG_SINGLE = 'S'
local TAG_TYPE = 'T'
local TAG_FLAG = 'N'
local TAG_MULTI_LINE = 'ML'
-- Docbunto processing patterns.
local patterns = {}
--- Configuration options.
-- @table options
-- @field[opt] {boolean} options.all Include local items in
-- documentation.
-- @field[opt] {boolean} options.autodoc Whether the module is being
-- called to automatically provide missing documentation.
-- @field[opt] {boolean} options.boilerplate Removal of
-- boilerplate (license block comments).
-- @field[opt] {string} options.caption Infobox image caption.
-- @field[opt] {boolean} options.code Only document Docbunto code
-- items - exclude article infobox and lede from
-- rendered documentation. Permits article to be
-- edited in VisualEditor.
-- @field[opt] {boolean} options.colon Format tags with a `:` suffix
-- and without the `@` prefix. This bypasses the "doctag
-- soup" some authors complain of.
-- @field[opt] {string} options.image Infobox image.
-- @field[opt] {boolean} options.box Include infobox in output.
-- @field[opt] {boolean} options.footer Append a horizontal line &
-- "generated by Docbunto" to the bottom of the output.
-- @field[opt] {boolean} options.noluaref Don't link to the [[mw:
-- Extension:Scribunto/Lua reference manual|Lua reference manual]] for types.
-- @field[opt] {boolean} options.card Include endmatter card in the
-- output at the end of the documentation.
-- @field[opt] {boolean} options.plain Disable Markdown formatting
-- in documentation.
-- @field[opt] {string} options.preface Preface text to insert
-- between lede & item documentation, used to provide
-- usage and code examples.
-- @field[opt] {boolean} options.simple Limit documentation to
-- descriptions only. Removes documentation of
-- subitem tags such as `@param` and `@field` ([[#Item
-- subtags|see list]]).
-- @field[opt] {boolean} options.sort Sort documentation items in
-- alphabetical order.
-- @field[opt] {boolean} options.strip Remove table index in
-- documentation.
-- @field[opt] {boolean} options.ulist Indent subitems as `<ul>`
-- lists (LDoc/JSDoc behaviour).
-- @field[opt] {boolean} options.verbose Show the code
-- description of a package function when documenting
-- function modules.
-- Docbunto private utilities.
local utils = {}
--- @{string.find} optimisation for @{string} functions.
-- @function utils.strfind_wrap
-- @param {function} strfunc String library function.
-- @return {function} Function wrapped in @{string.find} check.
-- @local
function utils.strfind_wrap(func)
return function(...)
local arg = {...}
if string.find(arg[1], arg[2]) then
return func(...);
end
end
end
--- String concatenation utility.
-- @function join
-- @param {sequence<string>} tbl Table of string segments
-- @return {function} A concatenated string.
-- @local
function utils.join(tbl)
return table.concat(tbl, '')
end
--- Pattern configuration function.
-- Resets patterns for each documentation build.
-- @function utils.configure_patterns
-- @param {options} options Configuration options.
-- @local
function utils.configure_patterns(options)
-- Setup Unicode or ASCII character encoding (optimisation).
gsub = utils.strfind_wrap(
options.unicode
and mw.ustring.gsub
or string.gsub
)
match = utils.strfind_wrap(
options.unicode
and mw.ustring.match
or string.match
)
patterns.DOCBUNTO_SUMMARY =
options.iso639_th
and '^[^ ]+'
or
options.unicode
and '^[^.։。।෴۔።]+[.։。।෴۔።]?'
or '^[^.]+%.?'
patterns.DOCBUNTO_CONCAT = options.iso639_th and '' or ' '
patterns.DOCBUNTO_TYPE = '^{({*[^}]+}*)}%s*'
patterns.DOCBUNTO_WIKITEXT = '^[{|!}:#*=]+[%s-}]+'
-- Setup parsing tag patterns with colon mode support.
patterns.DOCBUNTO_TAG = options.colon and '^%s*(%w+):' or '^%s*@(%w+)'
patterns.DOCBUNTO_TAG_COMMENT = '^[-%s]*' .. patterns.DOCBUNTO_TAG:sub(2)
patterns.DOCBUNTO_TAG_VALUE = patterns.DOCBUNTO_TAG .. '(.*)'
patterns.DOCBUNTO_TAG_MOD_VALUE = patterns.DOCBUNTO_TAG .. '%[([^%]]*)%](.*)'
patterns.DOCBUNTO_TAG_OPTION_VALUE = '(%[[^%]]*%])%s*(.*)'
end
--- Tag processor function.
-- @function utils.process_tag
-- @param {string} str Tag string to process.
-- @return {table} Tag object.
-- @local
function utils.process_tag(str)
local tag = {}
if str:find(patterns.DOCBUNTO_TAG_MOD_VALUE) then
tag.name, tag.modifiers, tag.value = str:match(patterns.DOCBUNTO_TAG_MOD_VALUE)
local modifiers = {}
for m in tag.modifiers:gmatch('[^%s,]+') do
local i = m:find('=')
if i then
local key = mw.text.trim(m:sub(1, i - 1))
local val = mw.text.trim(m:sub(i + 1, #m))
modifiers[key] = val
else
modifiers[m] = true
end
end
if modifiers.optchain then
modifiers.opt = true
modifiers.optchain = nil
end
tag.modifiers = modifiers
else
tag.name, tag.value = str:match(patterns.DOCBUNTO_TAG_VALUE)
end
tag.value = mw.text.trim(tag.value)
if p.tags._type_alias[tag.name] then
if p.tags._type_alias[tag.name] ~= 'variable' then
tag.value = p.tags._type_alias[tag.name] .. ' ' .. tag.value
tag.name = 'field'
end
if tag.value:match('^%S+') ~= '...' then
tag.value = tag.value:gsub('^(%S+)', '{%1}')
end
end
tag.name = p.tags._alias[tag.name] or tag.name
if tag.name ~= 'usage' and tag.value:find(patterns.DOCBUNTO_TYPE) then
tag.type = tag.value:match(patterns.DOCBUNTO_TYPE)
if tag.type:find('^%?') then
tag.type = tag.type:sub(2) .. '|nil'
end
tag.value = tag.value:gsub(patterns.DOCBUNTO_TYPE, '')
end
if (tag.name == 'param' or tag.name == 'field') and tag.value:find(patterns.DOCBUNTO_TAG_OPTION_VALUE) then
tag.value = tag.value:gsub(patterns.DOCBUNTO_TAG_OPTION_VALUE, '%1 %2')
tag.modifiers = tag.modifiers or {}
tag.modifiers.opt = true
end
if p.tags[tag.name] == TAG_FLAG then
tag.value = true
end
return tag
end
--- Module info extraction utility.
-- @function utils.extract_info
-- @param {table} documentation Package doclet info.
-- @return {table} Information name-value map.
-- @local
function utils.extract_info(documentation)
local info = {}
for _, tag in ipairs(documentation.tags) do
if p.tags._module_info[tag.name] then
if info[tag.name] then
if not info[tag.name]:find('^%* ') then
info[tag.name] = '* ' .. info[tag.name]
end
info[tag.name] = info[tag.name] .. '\n* ' .. tag.value
else
info[tag.name] = tag.value
end
end
end
return info
end
--- Type extraction utility.
-- @function utils.extract_types
-- @param {table} item Item documentation data.
-- @return {sequence<string>} Item types.
-- @local
function utils.extract_types(item)
local item_types = {}
for _, tag in ipairs(item.tags) do
if p.tags[tag.name] == TAG_TYPE then
item_types[1] = tag.name
if tag.name == 'variable' then
local implied_local = utils.process_tag('@local')
table.insert(item.tags, mw.clone(implied_local))
item.tags['local'] = mw.clone(implied_local)
end
if
p.tags._generic_tags[item_types[1]] and
not p.tags._project_level[item_types[1]] and
tag.type
then
item_types[#item_types + 1] = tag.type
end
break
end
end
return item_types
end
--- Name extraction utility.
-- @function utils.extract_name
-- @param {table} item Item documentation data.
-- @param {boolean} project Whether the item is project-level.
-- @return {string} Item name.
-- @local
function utils.extract_name(item, opts)
opts = opts or {}
local item_name
for _, tag in ipairs(item.tags) do
if p.tags[tag.name] == TAG_TYPE then
item_name = tag.value; break
end
end
if item_name or not opts.project then
return item_name
end
item_name = item.code:match('\nreturn%s+([%w_]+)')
if item_name == 'p' and not item.tags['alias'] then
local implied_alias = { name = 'alias', value = 'p' }
item.tags['alias'] = implied_alias
table.insert(item.tags, implied_alias)
end
item_name = (item_name and item_name ~= 'p')
and item_name
or item.filename
:gsub('^' .. mw.site.namespaces[828].name .. ':', '')
:gsub('^(%u)', mw.ustring.lower)
:gsub('/', '.'):gsub(' ', '_')
return item_name
end
--- Source code utility for item name detection.
-- @function utils.deduce_name
-- @param {string} tokens Stream tokens for first line.
-- @param {string} index Stream token index.
-- @param {table} opts Configuration options.
-- @param[opt] {boolean} opts.lookahead Whether a variable name succeeds the index.
-- @param[opt] {boolean} opts.lookbehind Whether a variable name precedes the index.
-- @return {string} Item name.
-- @local
function utils.deduce_name(tokens, index, opts)
local name = ''
if opts.lookbehind then
for i2 = index, 1, -1 do
if tokens[i2].type ~= 'keyword' then
name = tokens[i2].data .. name
else
break
end
end
elseif opts.lookahead then
for i2 = index, #tokens do
if tokens[i2].type ~= 'keyword' and not tokens[i2].data:find('^%(') then
name = name .. tokens[i2].data
else
break
end
end
end
return name
end
--- Parses Lua item names to extract a hierarchy.
--- @function utils.parse_hierarchy
-- @param {string} item_name Documentation item name.
-- @return {sequence<string>} List of global keys to index the
-- variable reference.
-- @local
function utils.parse_hierarchy(item_name)
local hierarchy = {}
for _, lexeme in ipairs(lexer(item_name)) do
if lexeme.type == 'ident' or lexeme.type == 'string' or lexeme.type == 'number' then
hierarchy[#hierarchy + 1] = lexeme.text
end
end
return hierarchy
end
--- Code analysis utility.
-- @function utils.code_heuristic
-- @param {table} item Item documentation data.
-- @local
function utils.code_heuristic(item)
local is_section = item.tags['private'] or item.tags['local'] or item.type == 'type'
if item.name and item.type and is_section then
return
end
local tokens = lexer(item.code:match('^[^\n]*'))[1]
local t, i = tokens[1], 1
local item_name, item_type
local lexemes = {}
while t do
if t.type ~= 'whitespace' then
lexemes[#lexemes + 1] = tokens[i]
end
t, i = tokens[i + 1], i + 1
end
t, i = lexemes[1], 1
while t do
if t.data == '=' and not item.name then
item_name = utils.deduce_name(lexemes, i - 1, { lookbehind = true })
end
if t.data == 'function' and not item.name then
item_type = 'function'
if lexemes[i + 1] and lexemes[i + 1].data ~= '(' then
item_name = utils.deduce_name(lexemes, i + 1, { lookahead = true })
end
end
if t.data == '{' or t.data == '{}' and not item.type then
item_type = 'table'
end
if t.data == 'local' and not is_section then
local implied_local = utils.process_tag('@local')
table.insert(item.tags, implied_local)
item.tags['local'] = mw.clone(implied_local)
end
t, i = lexemes[i + 1], i + 1
end
item.name = item.name or item_name or ''
item.type = item.type or item_type
if #(item.types or {}) == 0 then
item.types[1] = item.type
end
end
--- Array hash map conversion utility.
-- @function utils.hash_map
-- @param {table} item Item documentation data array.
-- @return {table} Item documentation data map.
-- @local
function utils.hash_map(array)
local map = array
for _, element in ipairs(array) do
if map[element.name] and not map[element.name].name then
table.insert(map[element.name], mw.clone(element))
elseif map[element.name] and map[element.name].name then
map[element.name] = { map[element.name], mw.clone(element) }
else
map[element.name] = mw.clone(element)
end
end
return map
end
--- Item export utility.
-- @function utils.export_item
-- @param {table} documentation Package documentation data.
-- @param {string} item_reference Identifier name for item.
-- @param {string} item_index Identifier name for item.
-- @param {string} item_alias Export alias for item.
-- @param {boolean} factory_item Whether the documentation item is a factory function.
-- @local
function utils.export_item(documentation, item_reference, item_index, item_alias, factory_item)
for _, item in ipairs(documentation.items) do
if item_reference == item.name then
item.tags['local'] = nil
item.tags['private'] = nil
for index, tag in ipairs(item.tags) do
if p.tags._privacy_tags[tag.name] then
table.remove(item.tags, index)
end
end
item.type, item.types[1] = 'member', 'member'
local accessor
if item_alias:find('^%[') then
accessor = ''
else
local is_class = documentation.type == 'classmod' or factory_item
if is_class and not item.tags['static'] then
accessor = ':'
else
accessor = '.'
end
end
if factory_item then
item.alias = utils.join{
documentation.items[item_index].tags['factory'].value,
accessor,
item_alias
}
else
item.alias = utils.join{
((documentation.tags['alias'] or {}).value or documentation.name),
accessor,
item_alias
}
end
item.hierarchy = utils.parse_hierarchy(item.alias)
break
end
end
end
--- Subitem tag correction utility.
-- @function utils.correct_subitem_tag
-- @param {table} item Item documentation data.
-- @local
function utils.correct_subitem_tag(item)
local field_tag = item.tags['field']
if item.type ~= 'function' or not field_tag then
return
end
if field_tag.name then
field_tag.name = 'param'
else
for _, tag_el in ipairs(field_tag) do
tag_el.name = 'param'
end
end
local param_tag = item.tags['param']
if param_tag and not param_tag.name then
if field_tag.name then
table.insert(param_tag, field_tag)
else
for _, tag_el in ipairs(field_tag) do
table.insert(param_tag, tag_el)
end
end
elseif param_tag and param_tag.name then
if field_tag.name then
param_tag = { param_tag, field_tag }
else
for i, tag_el in ipairs(field_tag) do
if i == 1 then
param_tag = { param_tag }
end
for _, tag_el in ipairs(field_tag) do
table.insert(param_tag, tag_el)
end
end
end
else
param_tag = field_tag
end
item.tags['field'] = nil
end
--- Item override tag utility.
-- @function utils.override_item_tag
-- @param {table} item Item documentation data.
-- @param {string} name Tag name.
-- @param[opt] {string} alias Target alias for tag.
-- @local
function utils.override_item_tag(item, name, alias)
if item.tags[name] then
item[alias or name] = item.tags[name].value
end
end
--- Identity function.
-- @function utils.identity
-- @param x The variable by reference.
-- @return The parameter `x` by reference.
-- @local
function utils.identity(x)
return x
end
--- Argument processing
-- @param {string} funcName name of function
-- @return function to process arguments
-- @local
local function makeInvokeFunc(funcName)
return function (f)
local args = require("Module:Arguments").getArgs(f, {
valueFunc = function (key, value)
if type(value) == 'string' then
value = value:match('^%s*(.-)%s*$') -- Remove whitespace.
if key == 'heading' or value ~= '' then
return value
else
return nil
end
else
return value
end
end
})
return p[funcName](args)
end
end
-- Docbunto rendering logic.
local frontend = {}
--- Markdown header converter.
-- @function urils.markdown_header
-- @param {string} hash Leading hash.
-- @param {string} text Header text.
-- @return {string} MediaWiki header.
-- @local
function frontend.markdown_header(hash, text)
local symbol = '='
return utils.join{
'\n',
symbol:rep(#hash), ' ', text, ' ', symbol:rep(#hash),
'\n'
}
end
--- Item reference formatting.
-- @function urils.item_reference
-- @param {string} ref Item reference.
-- @return {string} Internal MediaWiki link to article item.
-- @local
function frontend.item_reference(ref)
local temp = mw.text.split(ref, '|')
local item = temp[1]
local text = temp[2] or temp[1]
if references.items[item] then
local interwiki = 'mw'
item = interwiki .. references.items[item]
else
item = '#' .. item
end
return mw.text.tag{
name = 'code',
content = utils.join{ '[[', item, '|', text, ']]' }
}
end
--- Doclet type reference preprocessor.
-- Formats types with links to the [[mw:Extension:Scribunto/Lua reference manual|Lua reference manual]].
-- @function frontend.type_reference
-- @param {table} item Item documentation data.
-- @param {options} options Configuration options.
-- @local
function frontend.type_reference(item, options)
local interwiki = 'mw:'
if
not options.noluaref and
item.value and
item.value:match('^%S+') == mw.text.tag('code', nil, '...')
then
item.value = item.value:gsub('^(%S+)', mw.text.tag{
name = 'code',
content = utils.join{ '[[', interwiki, 'Lua reference manual#varargs|...]]' }
})
end
-- Handle tag type references.
item.types = item.types or item.type and { item.type }
if not item.types then
return
end
for index, item_type in ipairs(item.types) do
item.types[index] = item.types[index]:gsub('[%w_. ]+', function(item_type)
local data = references.types[item_type]
local name = data and data.name or item_type
if not name:match('%.') and not name:match('^%u') and data then
name = i18n:msg('type-' .. name)
end
if data and not options.noluaref then
return utils.join{ '[[', interwiki, data.link, '|', name , ']]' }
elseif
item_type:find(i18n:msg('line-no'):gsub('$1', ''))
then
return utils.join { '[[' , options.file , '#L-', item_type:sub(6), '|', name, ']]' }
elseif
not options.noluaref and
not p.tags._generic_tags[item_type]
then
return utils.join { '[[#', item_type, '|', name, ']]' }
end
end)
end
item.type = table.concat(item.types, i18n:msg('separator-dot'))
end
--- Markdown preprocessor to MediaWiki format.
-- @function frontend.markdown
-- @param {string} str Unprocessed Markdown string.
-- @return {string} MediaWiki-compatible markup with HTML formatting.
-- @local
function frontend.markdown(str)
-- Bold & italic tags.
str = str:gsub('%*%*%*([^\n*]+)%*%*%*', '<b><i>%1</i></b>')
str = str:gsub('%*%*([^\n*]+)%*%*', '<b>%1</b>')
str = str:gsub('%*([^\n*]+)%*', '<i>%1</i>')
-- Self-closing header support.
str = str:gsub('%f[^\n%z](#+) *([^\n#]+) *#+%s', frontend.markdown_header)
-- External and internal links.
str = str:gsub('%[([^\n%]]+)%]%(([^\n][^\n)]-)%)', '[%2 %1]')
str = str:gsub('%{@link%s+([^}]+)%}', '[%1]')
str = str:gsub('%@{([^\n}]+)}', frontend.item_reference)
-- Programming & scientific notation.
str = str:gsub('%f["`]`([^\n`]+)`%f[^"`]', '<code><nowiki>%1</nowiki></code>')
str = str:gsub('%$%$([^\n$]+)%$%$', '<math display="inline">%1</math>')
-- Strikethroughs.
str = str:gsub('~~([^\n~]+)~~', '<del>%1</del>')
-- HTML output.
return str
end
--- Doclet function item preprocessor.
-- Formats item name as a function call with top-level arguments.
-- @function frontend.pretask_function_name
-- @param {table} item Item documentation data.
-- @param {options} options Configuration options.
-- @local
function frontend.pretask_function_name(item, options)
local target = item.alias and 'alias' or 'name'
item[target] = item[target] .. '('
if
item.tags['param'] and
item.tags['param'].value and
not item.tags['param'].value:find('^[%w_]+[.[]')
then
if (item.tags['param'].modifiers or {})['opt'] then
local optional = mw.html.create('span')
optional:css('opacity', '0.65')
optional:wikitext(item.tags['param'].value:match('^(%S+)'))
item[target] = item[target] .. tostring(optional)
else
item[target] = item[target] .. item.tags['param'].value:match('^(%S*)')
end
elseif item.tags['param'] then
for index, tag in ipairs(item.tags['param']) do
if not tag.value:find('^[%w_]+[.[]') then
if (tag.modifiers or {})['opt'] then
local param = mw.html.create('span')
param:css('opacity', '0.65')
param:wikitext((index > 1 and ', ' or '') .. tag.value:match('^%S+'))
item[target] = item[target] .. tostring(param)
else
item[target] = item[target] .. (index > 1 and ', ' or '') .. tag.value:match('^(%S+)')
end
end
end
end
item[target] = item[target] .. ')'
end
--- Doclet parameter/field subitem preprocessor.
-- Indents and wraps variable prefix with `code` tag.
-- @function pretask_variable_prefix
-- @param {table} item Item documentation data.
-- @param {options} options Configuration options.
-- @local
function frontend.pretask_variable_prefix(item, options)
local indent_symbol = options.ulist and '*' or ':'
local indent_level, indentation, item_id
if item.value then
indent_level = item.value:match('^%S*') == '...'
and 0
or select(2, item.value:match('^%S*'):gsub('[.[]', ''))
indentation = indent_symbol:rep(indent_level)
item_id = options.item_id
item.value = indentation .. item.value:gsub(
'^(%S+)',
mw.text.tag{
name = 'code',
attrs = { id = item_id .. '~%1' },
content = '%1'
},
1
)
elseif item then
for _, item_el in ipairs(item) do
frontend.pretask_variable_prefix(item_el, options)
end
end
end
--- Doclet usage subitem preprocessor.
-- Formats usage example with `<syntaxhighlight>` tag.
-- @function frontend.pretask_usage_highlight
-- @param {table} item Item documentation data.
-- @param {options} options Configuration options.
-- @local
function frontend.pretask_usage_highlight(item, options)
if item.value then
item.value = unindent(mw.text.trim(item.value))
if item.value:find('^{{.+}}$') then
item.value = item.value:gsub('=', mw.text.nowiki)
local multi_line = item.value:find('\n') and '|m = 1|' or '|'
if item.value:match('^{{([^:]+)') == '#invoke' then
item.value = item.value:gsub('^{{#invoke:', '{{mlx' .. multi_line)
else
if options.entrypoint then
item.value = item.value:gsub('^([^|]+)|%s*([^|}]-)(%s*)([|}])','%1|"%2"%3%4')
end
item.value = item.value:gsub('^{{', '{{tlx' .. multi_line)
end
local highlight_class = tonumber(mw.site.currentVersion:match('^%d%.%d+')) > 1.19
and 'mw-highlight'
or 'mw-geshi'
if item.value:find('\n') then
local highlight_div = mw.html.create('div')
highlight_div.addClass(highlight_class)
highlight_div.addClass('mw-content-ltr')
highlight_div:attr('dir', 'ltr')
highlight_div:wikitext(item.value)
item.value = tostring(highlight_div)
else
item.value = mw.text.tag{
name = 'span',
attrs = { class = 'code' },
content = item.value
}
end
else
local highlight_tag = mw.html.create('syntaxhighlight')
highlight_tag:attr('lang', 'lua')
if not item.value:find('\n') then
highlight_tag:attr('inline', 'inline')
end
highlight_tag:wikitext(item.value)
item.value = tostring(highlight_tag)
end
elseif item then
for _, item_el in ipairs(item) do
frontend.pretask_usage_highlight(item_el, options)
end
end
end
--- Doclet error subitem preprocessor.
-- Formats line numbers (`{#}`) in error tag values.
-- @function frontend.pretask_error_line
-- @param {table} item Item documentation data.
-- @local
function frontend.pretask_error_line(item, options)
if item.name then
local line
if item.modifiers and item.modifiers.line then
line = item.modifiers.line
end
for mod in pairs(item.modifiers or {}) do
if mod:find('^%d+$') then line = mod end
end
if line then
if item.types then
item.types[#item.types + 1] = i18n:msg('line-no', line)
else
item.type = i18n:msg('line-no', line)
end
end
elseif item then
for _, item_el in ipairs(item) do
frontend.pretask_error_line(item_el, options)
end
end
end
--- Doclet item renderer.
-- @function frontend.render_item
-- @param {table} stream Wikitext documentation stream.
-- @param {table} item Item documentation data.
-- @param {options} options Configuration options.
-- @param[opt] {function} pretask Item data preprocessor.
-- @local
function frontend.render_item(stream, item, options, pretask)
if pretask then pretask(item, options) end
local item_name = item.alias or item.name
frontend.type_reference(item, options)
local item_type = item.type
for _, name in ipairs(p.tags._subtype_hierarchy) do
if item.tags[name] then
item_type = item_type .. i18n:msg('separator-dot') .. name
end
end
item_type = i18n:msg('parentheses', item_type)
if options.strip and item.export and item.hierarchy then
item_name = item_name:gsub('^[%w_]+[.[]?', '')
end
-- add single spaces between parentheses similar to
-- MediaWiki documentation
if not item_name:find( '()', 1, true ) then
item_name = item_name:gsub('%(', '( ')
item_name = item_name:gsub('%)', ' )')
end
item_name = item_name:gsub('%[', '[ ')
item_name = item_name:gsub('%]', ' ]')
local code_node = mw.html.create('code')
:attr('id', options.item_id)
:wikitext(item_name)
if not usingOptionsContent then
local span_node = stream:wikitext(';')
:tag('span')
:addClass('plainlinks docbunto-link')
if fileTooBig or not syntaxhighlightPresent then
span_node:wikitext('[{{fullurl:' .. options.file .. '|action=edit#mw-ce-l' .. item.lineno .. '}} ' .. tostring(code_node) .. ']')
elseif title.namespace == 828 and options.file == title.text then
span_node:wikitext('[[' .. '#L-' .. item.lineno .. '|' .. tostring(code_node) .. ']]')
else
span_node:wikitext('[[' .. options.file .. '#L-' .. item.lineno .. '|' .. tostring(code_node) .. ']]')
end
else
stream:wikitext(';' .. tostring(code_node))
end
stream
:wikitext(item_type)
:newline()
if (#(item.summary or '') + #item.description) ~= 0 then
local separator = #(item.summary or '') ~= 0 and #item.description ~= 0
and (item.description:find(patterns.DOCBUNTO_WIKITEXT) and '\n' or patterns.DOCBUNTO_CONCAT)
or ''
local intro = (item.summary or '') .. separator .. item.description
intro = intro
:gsub('\n([{:#*])', '\n:%1')
:gsub('\n\n([^=])', '\n:%1')
stream:wikitext(':' .. intro):newline()
end
end
--- Doclet tag renderer.
-- @function frontend.render_tag
-- @param {table} stream Wikitext documentation stream.
-- @param {string} name Item tag name.
-- @param {table} tag Item tag data.
-- @param {options} options Configuration options.
-- @param[opt] {function} pretask Item data preprocessor.
-- @local
function frontend.render_tag(stream, name, tag, options, pretask)
if pretask then pretask(tag, options) end
if tag.value then
frontend.type_reference(tag, options)
local tag_name = i18n:msg('tag-' .. name, '1')
-- Handle ul/ol/dl comment support.
stream:wikitext(':')
:tag('b')
:wikitext(tag_name)
:done():done()
:wikitext(i18n:msg('separator-semicolon'))
:wikitext((mw.text.trim(tag.value):gsub('\n([{:#*])', '\n:%1')))
if tag.value:find('\n[{:#*]') and (tag.type or (tag.modifiers or {})['opt']) then
stream:newline():wikitext(':')
end
-- Type and optional appositive for tag.
if tag.type and (tag.modifiers or {})['opt'] then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = {
tag.type ..
i18n:msg('separator-colon') ..
i18n:msg('optional')
}
})
elseif tag.type then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = { tag.type }
})
elseif (tag.modifiers or {})['opt'] then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = { i18n:msg('optional') }
})
end
stream:newline()
else
local tag_name = i18n:msg('tag-' .. name, tostring(#tag))
stream
:wikitext(':')
:tag('b')
:wikitext(tag_name)
:done():done()
:wikitext(i18n:msg('separator-semicolon'))
:newline()
for _, tag_el in ipairs(tag) do
frontend.type_reference(tag_el, options)
local marker = options.ulist and '*' or ':'
stream:wikitext(':' .. marker .. tag_el.value:gsub('\n([{:#*])', '\n:' .. marker .. '%1'))
if tag_el.value:find('\n[{:#*]') and (tag_el.type or (tag_el.modifiers or {})['opt']) then
stream:newline():wikitext(':' .. marker .. (tag_el.value:match('^[*:]+') or ''))
end
if tag_el.type and (tag_el.modifiers or {})['opt'] then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = {
tag_el.type ..
i18n:msg('separator-colon') ..
i18n:msg('optional')
}
})
elseif tag_el.type then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = { tag_el.type }
})
elseif (tag_el.modifiers or {})['opt'] then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = { i18n:msg('optional') }
})
end
stream:newline()
end
end
end
--- Template entrypoint for [[Template:Docbunto]].
-- @function p._main
-- @param {table} args Arguments
-- @return {string} Module documentation output.
function p._main(args)
frame = mw.getCurrentFrame()
local modname = mw.text.trim(args[1] or args.file or DEFAULT_TITLE)
local options = {}
options.file = modname
options.all = yesno(args.all or false)
options.autodoc = yesno(args.autodoc or false)
options.boilerplate = yesno(args.boilerplate or options.autodoc or false)
options.box = yesno(args.box or args.infobox or options.autodoc or false)
options.caption = args.caption
options.card = yesno(args.card or (title.namespace == 828 and not options.autodoc))
options.code = yesno(args.code or false)
options.colon = yesno(args.colon or false)
options.footer = yesno(args.footer or title.namespace == 828)
options.image = args.image
options.migration = yesno(args.migration or false)
options.noluaref = yesno(args.noluaref or false)
options.plain = yesno(args.plain or false)
options.preface = args.preface
options.simple = yesno(args.simple or false)
options.sort = yesno(args.sort or false)
options.strip = yesno(args.strip or false)
options.verbose = yesno(args.verbose or false)
options.ulist = yesno(args.ulist or false)
return p.build(modname, options)
end
--- Entrypoint for the module.
-- @function p.main
-- @param {Frame} frame Module frame.
-- @return {string} Module documentation output.
p.main = makeInvokeFunc('_main')
--- Scribunto documentation generator entrypoint.
-- @function p.build
-- @param[opt] {string} modname Module page name (without namespace).
-- Default: second-level subpage.
-- @param[opt] {options} options Configuration options.
function p.build(modname, options)
modname = modname or DEFAULT_TITLE
options = options or {}
local tagdata = p.taglet(modname, options)
local docdata = p.doclet(tagdata, options)
return docdata
end
--- Docbunto taglet parser for Scribunto modules.
-- @function p.taglet
-- @param[opt] {string} modname Module page name (without namespace).
-- @param[opt] {options} options Configuration options.
-- @error[line=1081] {string} 'Lua source code not found in $1'
-- @error[line=1087] {string} 'documentation markup for Docbunto not found in $1'
-- @return {table} Module documentation data.
function p.taglet(modname, options)
modname = modname or DEFAULT_TITLE
options = options or {}
local filepath = mw.site.namespaces[828].name .. ':' .. modname
local content = mw.title.new(filepath):getContent()
-- Content checks.
if options.content then
content = options.content
usingOptionsContent = true
else
content = mw.title.new(filepath):getContent() or error(i18n:msg('no-content', filepath), 0)
end
-- Syntax highlighting is disabled for files above a specific size
fileTooBig = content:len() > 102400
if
not content:match('%-%-%-') and
not content:match(options.colon and '%w+:' or '@%w+')
then
error(i18n:msg('no-markup', filepath))
end
-- Remove leading escapes.
content = content:gsub('^%-%-+%s*<[^>]+>\n', '')
-- Remove closing pretty comments.
content = content:gsub('\n%-%-%-%-%-+(\n[^-]+)', '\n-- %1')
-- Remove boilerplate block comments.
if options.boilerplate then
content = content:gsub('^%-%-%[=*%[\n.-\n%-?%-?%]%=*]%-?%-?%s+', '')
content = content:gsub('%s+%-%-%[=*%[\n.-\n%-?%-?%]%=*]%-?%-?$', '')
end
-- Configure patterns for colon mode and Unicode character encoding.
options.unicode = type(content:find('[^%w%c%p%s]+')) == 'number'
options.iso639_th = type(content:find('\224\184[\129-\155]')) == 'number'
utils.configure_patterns(options)
-- Content lexing.
local lines = lexer(content)
local tokens = {}
local dummy_token = {
data = '',
posFirst = 1,
posLast = 1
}
local token_closure = 0
for _, line in ipairs(lines) do
if #line == 0 then
dummy_token.type = token_closure == 0
and 'whitespace'
or tokens[#tokens].type
table.insert(tokens, mw.clone(dummy_token))
else
for _, token in ipairs(line) do
if token.data:find('^%[=*%[$') or token.data:find('^%-%-%[=*%[$') then
token_closure = 1
end
if token.data:find(']=*]') then
token_closure = 0
end
table.insert(tokens, token)
end
end
end
-- Start documentation data.
local documentation = {}
documentation.filename = filepath
documentation.description = ''
documentation.code = content
documentation.comments = {}
documentation.tags = {}
documentation.items = {}
local line_no = 0
local item_index = 0
-- Taglet tracking variables.
local start_mode = true
local comment_mode = false
local doctag_mode = false
local export_mode = false
local special_tag = false
local pragma_mode = false
local factory_mode = false
local return_mode = false
local comment_tail = ''
local tag_name = ''
local new_item = false
local new_tag = false
local new_tag_item = false
local new_item_code = false
local pretty_comment = false
local comment_brace = false
local t, i, item = tokens[1], 1
local p_success, p_error = pcall(function()
while t do
-- Taglet variable update.
new_item = t.data:find('^%-%-%-') or t.data:find('^%-%-%[%=*%[$')
comment_tail = t.data:gsub('^%-%-+', '')
tag_name = comment_tail:match(patterns.DOCBUNTO_TAG)
tag_name = p.tags._alias[tag_name] or tag_name
new_tag = type(p.tags[tag_name]) == 'string'
local last_token = t.posFirst ~= 1 and 2 or 1
new_tag_item = (
new_tag and i > 1 and tokens[i - last_token].type ~= 'comment'
and not tokens[i - last_token].data:find(patterns.DOCBUNTO_TAG_COMMENT)
)
pretty_comment =
t.data:find('^%-%-%-+%s*$') or
t.data:find('[^-]+%-%-%-+%s*$') or
t.data:find('%-*[ \t]*<nowiki>$') or
t.data:find('%-*[ \t]*<pre>$')
comment_brace =
t.data:find('^%-%-%[=*%[$') or
t.data:find('^%-%-%]=*%]$') or
t.data:find('^%]=*%]%-%-$')
pragma_mode = tag_name == 'pragma'
export_mode = tag_name == 'export' and not doctag_mode
special_tag = pragma_mode or export_mode
local tags, tag, subtokens, separator
-- Line counter.
if t.posFirst == 1 then
line_no = line_no + 1
end
-- Data insertion logic.
if t.type == 'comment' then
if new_item then
comment_mode = true
end
if (new_tag or new_tag_item) and not special_tag then
comment_mode = true
doctag_mode = true
end
-- Module-level documentation taglet.
if start_mode then
table.insert(documentation.comments, t.data)
end
-- Module description.
if
start_mode and comment_mode and
not new_tag and not doctag_mode and
not pretty_comment and not comment_brace
then
separator = mw.text.trim(comment_tail):find(patterns.DOCBUNTO_WIKITEXT)
and '\n'
or (#documentation.description ~= 0 and patterns.DOCBUNTO_CONCAT or '')
documentation.description = utils.join{
documentation.description,
separator,
mw.text.trim(comment_tail)
}
end
-- Switch from module description to module-level tag list.
if start_mode and new_tag and not export_mode then
doctag_mode = true
table.insert(documentation.tags, utils.process_tag(comment_tail))
-- Concatenating module-level tag with more information.
elseif
start_mode and doctag_mode and
not pretty_comment and not comment_brace
then
tags = documentation.tags
if p.tags[tags[#tags].name] == TAG_MULTI then
separator = mw.text.trim(comment_tail):find(patterns.DOCBUNTO_WIKITEXT)
and '\n'
or patterns.DOCBUNTO_CONCAT
tags[#tags].value = utils.join{
tags[#tags].value,
separator,
mw.text.trim(comment_tail)
}
elseif p.tags[tags[#tags].name] == TAG_MULTI_LINE then
tags[#tags].value = utils.join{
tags[#tags].value,
'\n',
comment_tail
}
end
end
-- Detect a new documentation item.
if not start_mode and (new_item or new_tag_item) and not special_tag then
table.insert(documentation.items, {})
item_index = item_index + 1
item = documentation.items[item_index]
item.lineno = line_no
item.code = ''
item.comments = {}
item.description = ''
item.tags = {}
end
-- Concatenate an item with more information.
if
not start_mode and comment_mode and
not new_tag and not doctag_mode and
not pretty_comment and not comment_brace
then
separator = (mw.text.trim(comment_tail):find(patterns.DOCBUNTO_WIKITEXT))
and '\n'
or (#item.description ~= 0 and patterns.DOCBUNTO_CONCAT or '')
item.description = utils.join{
item.description,
separator,
mw.text.trim(comment_tail)
}
end
-- Switch from item information to item tags.
if not start_mode and new_tag and not special_tag then
doctag_mode = true
table.insert(item.tags, utils.process_tag(comment_tail))
-- Extend item tags with lines of further information.
elseif
not start_mode and doctag_mode and
not pretty_comment and not comment_brace
then
tags = item.tags
if p.tags[tags[#tags].name] == TAG_MULTI then
separator = mw.text.trim(comment_tail):find(patterns.DOCBUNTO_WIKITEXT)
and '\n'
or patterns.DOCBUNTO_CONCAT
tags[#tags].value = utils.join{
tags[#tags].value,
separator,
mw.text.trim(comment_tail)
}
elseif p.tags[tags[#tags].name] == TAG_MULTI_LINE then
tags[#tags].value = utils.join{
tags[#tags].value,
'\n',
comment_tail
}
end
end
-- Save pre-item comments to item information.
if not start_mode and (comment_mode or doctag_mode) then
table.insert(item.comments, t.data)
end
-- Export tag support.
if export_mode then
factory_mode = t.posFirst ~= 1
if factory_mode then
item.exports = true
else
documentation.exports = true
end
-- Parse export token data while concatenating code.
subtokens = {}
while t and (not factory_mode or (factory_mode and t.data ~= 'end')) do
if factory_mode then
item.code = utils.join{
item.code,
(t.posFirst == 1 and '\n' or ''),
t.data
}
end
-- Fetch our item export tokens aka "subtokens".
t, i = tokens[i + 1], i + 1
if t and t.posFirst == 1 then
line_no = line_no + 1
end
if t and t.type ~= 'whitespace' and t.type ~= 'keyword' and t.type ~= 'comment' then
table.insert(subtokens, t)
end
end
-- Tracking and boolean variables for parsing exports.
local separator = { [','] = true, [';'] = true }
local brace = { ['{'] = true, ['}'] = true }
local item_reference, item_alias = '', ''
local sequence_index, has_key = 0, false
local subtoken, index = subtokens[2], 2
-- Logic that exports items to an item or module.
while not brace[subtoken.data] do
if subtoken.data == '=' then
has_key = true
elseif not separator[subtoken.data] then
if has_key then
item_reference = item_reference .. subtoken.data
else
item_alias = item_alias .. subtoken.data
end
end
if subtokens[index + 1] and separator[subtokens[index + 1].data] or brace[subtokens[index + 1].data] then
if not has_key then
sequence_index = sequence_index + 1
item_reference, item_alias = item_alias, item_reference
item_alias = '[' .. tostring(sequence_index) .. ']'
end
utils.export_item(documentation, item_reference, item_index, item_alias, factory_mode)
item_reference, item_alias, has_key = '', '', false
end
subtoken, index = subtokens[index + 1], index + 1
end
if not factory_mode then
break -- end of file
else
factory_mode = false -- end of item
end
end
-- Pragma tag support.
if pragma_mode then
tag = utils.process_tag(comment_tail)
if tag.modifiers then
options[tag.value] = yesno((next((tag ).modifiers or {})), true)
elseif tag.value:find(' ') then
local index = tag.value:find(' ')
local key = tag.value:sub(1, index - 1)
local val = tag.value:sub(index + 1)
options[key] = yesno(val)
elseif options[tag.value] == nil then
options[tag.value] = true
end
end
end
-- Package data post-processing.
if (t.type ~= 'comment' or t.data:find('^%-%-%]=*%]$') or t.data:find('^%]=*%]%-%-$')) and (comment_mode or doctag_mode) and start_mode then
documentation.tags = utils.hash_map(documentation.tags)
documentation.name = utils.extract_name(documentation, { project = true })
documentation.info = utils.extract_info(documentation)
documentation.types = utils.extract_types(documentation) or { 'module' }
documentation.type = documentation.types[1]
-- N.B: summary field = 1st comment sentence; description = text remainder.
if #documentation.description ~= 0 then
documentation.summary = match(documentation.description, patterns.DOCBUNTO_SUMMARY)
documentation.description = documentation.description:sub(select(2, documentation.description:find('%s*', #documentation.summary + 1)) + 1)
end
documentation.description = documentation.description:gsub('%s%s+', '\n\n')
documentation.executable = p.tags._code_types[documentation.type] and true or false
utils.correct_subitem_tag(documentation)
utils.override_item_tag(documentation, 'name')
utils.override_item_tag(documentation, 'alias')
utils.override_item_tag(documentation, 'summary')
utils.override_item_tag(documentation, 'description')
utils.override_item_tag(documentation, 'class', 'type')
end
-- Item data post-processing.
if
(t.type ~= 'comment' or t.data:find('^%-%-%]=*%]$') or t.data:find('^%]=*%]%-%-$'))
and (comment_mode or doctag_mode) and item_index ~= 0
then
item.tags = utils.hash_map(item.tags)
item.name = utils.extract_name(item)
item.types = utils.extract_types(item)
item.type = item.types[1]
if #item.description ~= 0 then
item.summary = match(item.description, patterns.DOCBUNTO_SUMMARY)
item.description = item.description:sub(select(2, item.description:find('%s*', #item.summary + 1)) + 1)
end
item.description = item.description:gsub('%s%s+', '\n\n')
new_item_code = true
end
-- Documentation block reset.
if t.type ~= 'comment' or t.data:find('^%-%-%]=*%]$') or t.data:find('^%]=*%]%-%-$') then
start_mode = false
comment_mode = false
doctag_mode = false
export_mode = false
pragma_mode = false
end
-- Don't concatenate module return value into item code.
if t.data == 'return' and t.posFirst == 1 then
return_mode = true
end
-- Item code concatenation.
if item_index ~= 0 and not doctag_mode and not comment_mode and not comment_brace and not return_mode then
separator = #item.code ~= 0 and t.posFirst == 1 and '\n' or ''
item.code = utils.join{ item.code, separator, t.data }
-- Code analysis on item headline.
if new_item_code and item.code:find('\n') and t.posFirst == 1 then
utils.code_heuristic(item)
new_item_code = false
end
end
t, i = tokens[i + 1], i + 1
end
documentation.lineno = line_no
local package_name = (documentation.tags['alias'] or {}).value or documentation.name or DEFAULT_VARIABLE
local package_alias = (documentation.tags['alias'] or {}).value or 'p'
local export_ptn = '^%s([.[])'
for _, item in ipairs(documentation.items) do
if item.name == package_alias or (item.name and item.name:match('^' .. package_alias .. '[.[]')) then
item.alias = item.name:gsub(export_ptn:format(package_alias), documentation.name .. '%1')
end
if
item.name == package_name or
item.tags['export'] or
(item.name and package_name and item.name:find(export_ptn:format(package_name))) or
(item.alias and package_name and item.alias:find(export_ptn:format(package_name)))
then
item.export = true
item.tags['local'] = nil
item.tags['private'] = nil
for index, tag in ipairs(item.tags) do
if tag.name == 'local' or tag.name == 'private' then
table.remove(item.tags, index)
end
end
end
if item.name and (item.name:find('[.:]') or item.name:find('%[[\'"]')) then
item.hierarchy = utils.parse_hierarchy(item.name)
end
item.type = item.type or ((item.alias or item.name or ''):find('[.[]') and 'member' or 'variable')
utils.correct_subitem_tag(item)
utils.override_item_tag(item, 'name')
utils.override_item_tag(item, 'alias')
utils.override_item_tag(item, 'summary')
utils.override_item_tag(item, 'description')
utils.override_item_tag(item, 'class', 'type')
end
-- Sort documentation items by their access level.
-- Items are sorted by whether they are exported at the module level.
-- Items are then subsorted by whether the item is `@local` aka `@private`.
table.sort(documentation.items, function(item1, item2)
local inaccessible1 = item1.tags['local'] or item1.tags['private']
local inaccessible2 = item2.tags['local'] or item2.tags['private']
-- Send package items to the top.
if item1.export and not item2.export then
return true
elseif item2.export and not item1.export then
return false
-- Send private items to the bottom.
elseif inaccessible1 and not inaccessible2 then
return false
elseif inaccessible2 and not inaccessible1 then
return true
-- Optional alphabetical sort.
elseif options.sort then
return (item1.alias or item1.name) < (item2.alias or item2.name)
-- Sort via source code order by default.
else
return item1.lineno < item2.lineno
end
end)
end)
if not p_success then
mw.addWarning("Docbunto error: " .. p_error)
end
return documentation
end
--- Doclet renderer for Docbunto taglet data.
-- @function p.doclet
-- @param {table} data Taglet documentation data.
-- @param[opt] {options} options Configuration options.
-- @return {string} Wikitext documentation output.
function p.doclet(data, options)
local documentation = mw.html.create()
local namespace_ptn = utils.join{ '^', mw.site.namespaces[828].name, ':' }
local codepage = data.filename:gsub(namespace_ptn, '', 1)
options = options or {}
options.file = data.filename
frame = frame or mw.getCurrentFrame():getParent() or mw.getCurrentFrame()
local maybe_md = options.plain and utils.identity or frontend.markdown
local tohtml = function(str)
return frame:preprocess(maybe_md(str))
end
-- Disable edit sections for automatic documentation pages.
if not options.code then
documentation:wikitext(frame:preprocess('__NOEDITSECTION__'))
end
-- Information
if not options.code and options.box then
local custom, infobox = pcall(require, 'Module:Docbunto/infobox')
if custom and type(infobox) == 'function' then
documentation:wikitext(infobox(data, codepage, frame, options, title, maybe_md)):newline()
end
end
-- Documentation lede.
if not options.code and (#(data.summary or '') + #data.description) ~= 0 then
local separator = #(data.summary or '') ~= 0 and #data.description ~= 0
and (data.description:find(patterns.DOCBUNTO_WIKITEXT) and '\n\n' or patterns.DOCBUNTO_CONCAT)
or ''
local intro = utils.join{ data.summary or '', separator, data.description }
intro = tohtml(intro:gsub('^(' .. codepage .. ')', mw.text.tag('b', nil, '%1')))
if options.migration then
documentation:wikitext(frame:preprocess(mw.text.tag{
name = 'syntaxhighlight',
content = intro
})):newline():newline()
else
documentation:wikitext(intro):newline():newline()
end
end
-- Custom documentation preface.
if options.preface then
documentation:wikitext(options.preface):newline():newline()
end
-- Start code documentation.
local codedoc = mw.html.create()
local function_module = data.tags['param'] or data.tags['return']
local header_type =
documentation.type == 'classmod'
and 'class'
or function_module
and 'function'
or 'items'
if (function_module or #data.items ~= 0) and not options.code or options.preface then
codedoc
:tag('h2')
:wikitext(i18n:msg('header-documentation'))
:done()
:newline()
end
if (function_module or #data.items ~= 0) then
codedoc
:tag('h3')
:wikitext(i18n:msg('header-' .. header_type))
:done()
:newline()
end
-- Function module support.
if function_module then
data.type, data.types[1] = 'function', 'function'
options.item_id = data.name
if not options.code or not options.verbose then data.description = '' end
frontend.render_item(codedoc, data, options, frontend.pretask_function_name)
if not options.simple and data.tags['param'] then
frontend.render_tag(codedoc, 'param', data.tags['param'], options, frontend.pretask_variable_prefix)
end
if not options.simple and data.tags['error'] then
frontend.render_tag(codedoc, 'error', data.tags['error'], options, frontend.pretask_error_line)
end
if not options.simple and data.tags['return'] then
frontend.render_tag(codedoc, 'return', data.tags['return'], options)
end
end
-- Render documentation items.
local other_header = false
local private_header = false
local inaccessible
for _, item in ipairs(data.items) do
inaccessible = item.tags['local'] or item.tags['private']
if not options.all and inaccessible then
break
end
if
not other_header and item.type ~= 'section' and item.type ~= 'type' and
not item.export and not item.hierarchy and not inaccessible
then
codedoc
:tag('h3')
:wikitext(i18n:msg('header-other'))
:done()
:newline()
other_header = true
end
if not private_header and options.all and inaccessible then
codedoc
:tag('h3')
:wikitext(i18n:msg('header-private'))
:done()
:newline()
private_header = true
end
if item.type == 'section' then
codedoc
:tag('h3')
:wikitext((mw.ustring.gsub(item.summary or item.alias or item.name, '[.։。।෴۔።]$', '')))
:done()
:newline()
if #item.description ~= 0 then
codedoc:wikitext(mw.text.trim(item.description)):newline()
end
elseif item.type == 'type' then
if options.strip and item.export and item.hierarchy then
if item.alias then
item.alias = item.alias:gsub('^[%w_]+[.[]?', '')
else
item.name = item.name:gsub('^[%w_]+[.[]?', '')
end
end
codedoc
:tag('h3')
:wikitext(mw.text.tag('code', {}, item.alias or item.name))
:done()
:newline()
if (#(item.summary or '') + #item.description) ~= 0 then
local separator = #(item.summary or '') ~= 0 and #item.description ~= 0
and (item.description:find(patterns.DOCBUNTO_WIKITEXT) and '\n\n' or patterns.DOCBUNTO_CONCAT)
or ''
codedoc:wikitext((item.summary or '') .. separator .. item.description):newline()
end
elseif item.type == 'function' then
options.item_id = item.alias or item.name
frontend.render_item(codedoc, item, options, frontend.pretask_function_name)
if not options.simple and item.tags['param'] then
frontend.render_tag(codedoc, 'param', item.tags['param'], options, frontend.pretask_variable_prefix)
end
if not options.simple and item.tags['error'] then
frontend.render_tag(codedoc, 'error', item.tags['error'], options, frontend.pretask_error_line)
end
if not options.simple and item.tags['return'] then
frontend.render_tag(codedoc, 'return', item.tags['return'], options)
end
elseif
item.type == 'table' or
item.type ~= nil and (
item.type == 'member' or
item.type == 'variable'
) and (item.alias or item.name)
then
options.item_id = item.alias or item.name
frontend.render_item(codedoc, item, options)
if not options.simple and item.tags['field'] then
frontend.render_tag(codedoc, 'field', item.tags['field'], options, frontend.pretask_variable_prefix)
end
end
if item.type ~= 'section' and item.type ~= 'type' then
if not options.simple and item.tags['note'] then
frontend.render_tag(codedoc, 'note', item.tags['note'], options)
end
if not options.simple and item.tags['warning'] then
frontend.render_tag(codedoc, 'warning', item.tags['warning'], options)
end
if not options.simple and item.tags['fixme'] then
frontend.render_tag(codedoc, 'fixme', item.tags['fixme'], options)
end
if not options.simple and item.tags['todo'] then
frontend.render_tag(codedoc, 'todo', item.tags['todo'], options)
hasTodoItems = true
end
if not options.simple and item.tags['usage'] then
frontend.render_tag(codedoc, 'usage', item.tags['usage'], options, frontend.pretask_usage_highlight)
end
if not options.simple and item.tags['see'] then
frontend.render_tag(codedoc, 'see', item.tags['see'], options)
end
end
end
-- Render module-level annotations.
local header_level = options.code and 'h3' or 'h2'
local header_text
for _, tag_name in ipairs(p.tags._annotation_hierarchy) do
if data.tags[tag_name] then
header_text = i18n:msg('tag-' .. tag_name, data.tags[tag_name].value and '1' or '2')
header_text = '<' .. header_level .. '>' .. ' ' .. header_text .. ' ' .. '</' .. header_level .. '>'
codedoc:newline():wikitext(header_text):newline()
if data.tags[tag_name].value then
codedoc:wikitext(data.tags[tag_name].value):newline()
else
for _, tag_el in ipairs(data.tags[tag_name]) do
codedoc:wikitext('* ' .. tag_el.value):newline()
end
end
end
end
-- Byline for autogeneration.
if options.footer then
codedoc:newline()
codedoc:tag('hr'):done()
codedoc:newline()
codedoc:tag('p'):css( 'text-align', 'right' ):wikitext(i18n:msg('message-autogeneration'))
end
-- Add nowiki tags for EOF termination in tests.
codedoc:tag('nowiki', { selfClosing = true })
-- Code documentation formatting.
codedoc = tohtml(tostring(codedoc))
documentation:wikitext(codedoc)
-- Endmatter table for module information.
if options.card then
local endmatter = mw.html.create('table')
endmatter:addClass('wikitable')
endmatter:css('width', '75%')
endmatter:css('margin', '0 auto 1em auto')
local module_type = data.type == 'module'
and i18n:msg('invocable-module')
or i18n:msg('meta-module')
endmatter
:tag('caption')
:wikitext('Module information card (autogenerated)')
endmatter
:tag('tr')
:tag('th'):attr('scope', 'col'):wikitext('Name'):done()
:tag('th'):attr('scope', 'col'):wikitext('Value'):done()
if options.image or data.info and data.info['image'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('endmatter-image')):done()
:tag('td')
:attr('align', 'center')
:wikitext('[[File:' .. (options.image or data.info['image']) .. '|270px|center]]')
:done()
:done()
end
if options.caption or data.info and data.info['caption'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('endmatter-caption')):done()
:tag('td')
:attr('align', options.image or data.info['image'] and 'center' or 'left')
:wikitext(tohtml(options.caption or data.info['caption']))
:done()
:done()
end
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext((gsub(i18n:msg('type-variable'), '^%w', mw.ustring.upper))):done()
:tag('td')
:attr('align', options.image or data.info and data.info['image'] and 'center' or 'left')
:wikitext(mw.text.tag({
name = options.image or data.info and data.info['image'] and 'h1' or 'span',
content = mw.text.tag({
name = 'b',
content = mw.text.tag({
name = 'code',
content = data.name or DEFAULT_VARIABLE
})
})
})):done()
:done()
:tag('tr')
:tag('td'):wikitext(i18n:msg('code')):done()
:tag('td'):wikitext(tohtml('[[' .. data.filename .. ']]')):done()
:done()
if data.info and data.info['release'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('release-status')):done()
:tag('td'):wikitext(tohtml(data.info['release'])):done()
:done()
end
if data.summary then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('description')):done()
:tag('td'):wikitext(tohtml(data.summary)):done()
:done()
end
if data.info and data.info['author'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('author')):done()
:tag('td'):newline():wikitext(tohtml(data.info['author'])):done()
:done()
end
if data.info and data.info['attribution'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('using-code-by')):done()
:tag('td'):wikitext(tohtml(data.info['attribution'])):done()
:done()
end
if data.info and data.info['credit'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('other-attribution')):done()
:tag('td'):wikitext(tohtml(data.info['credit'])):done()
:done()
end
if data.info and data.info['require'] then
data.info['require'] = data.info['require']
:gsub('^[^[%s]+$', '[[%1]]')
:gsub('%* ([^[%s]+)', '* [[%1]]')
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('dependencies')):done()
:tag('td'):newline():wikitext(tohtml(data.info['require'])):done()
:done()
end
if data.info and data.info['demo'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('examples')):done()
:tag('td'):wikitext(tohtml(data.info['demo'])):done()
:done()
end
endmatter:newline()
endmatter = tostring(endmatter)
documentation:newline():newline()
documentation:wikitext(endmatter)
end
documentation = tostring(documentation)
if hasTodoItems and options.autodoc
and mw.title.new('Module:' .. options.file).fullText == mw.title.getCurrentTitle().fullText then
documentation = documentation .. '[[Category:' .. i18n:msg('todo') .. ']]'
end
if mw.title.new('Module:Docbunto/styles.css').exists then
documentation = documentation .. frame:extensionTag('templatestyles', '', {src = 'Module:Docbunto/styles.css'})
end
return documentation
end
--- Token dictionary for Docbunto tags.
-- Maps Docbunto tag names to tag tokens.
-- * Multi-line tags use the `'M'` token.
-- * Multi-line preformatted tags use the `'ML'` token.
-- * Identifier tags use the `'ID'` token.
-- * Single-line tags use the `'S'` token.
-- * Flags use the `'N'` token.
-- * Type tags use the `'T'` token.
-- @table p.tags
p.tags = {
-- Item-level tags, available for global use.
['param'] = 'M', ['see'] = 'M', ['note'] = 'M', ['usage'] = 'ML',
['description'] = 'M', ['field'] = 'M', ['return'] = 'M',
['fixme'] = 'M', ['todo'] = 'M', ['warning'] = 'M', ['error'] = 'M';
['class'] = 'ID', ['name'] = 'ID', ['alias'] = 'ID';
['summary'] = 'S', ['pragma'] = 'S', ['factory'] = 'S',
['release'] = 'S', ['author'] = 'S', ['copyright'] = 'S', ['license'] = 'S',
['image'] = 'S', ['caption'] = 'S', ['require'] = 'S', ['attribution'] = 'S',
['credit'] = 'S', ['demo'] = 'S';
['local'] = 'N', ['export'] = 'N', ['private'] = 'N', ['constructor'] = 'N',
['static'] = 'N';
-- Project-level tags, all scoped to a file.
['module'] = 'T', ['script'] = 'T', ['classmod'] = 'T', ['topic'] = 'T',
['submodule'] = 'T', ['example'] = 'T', ['file'] = 'T';
-- Module-level tags, used to register module items.
['function'] = 'T', ['table'] = 'T', ['member'] = 'T', ['variable'] = 'T',
['section'] = 'T', ['type'] = 'T';
}
p.tags._alias = {
-- Normal aliases.
['about'] = 'summary',
['abstract'] = 'summary',
['brief'] = 'summary',
['bug'] = 'fixme',
['argument'] = 'param',
['credits'] = 'credit',
['code'] = 'usage',
['details'] = 'description',
['discussion'] = 'description',
['exception'] = 'error',
['lfunction'] = 'function',
['package'] = 'module',
['property'] = 'member',
['raise'] = 'error',
['requires'] = 'require',
['returns'] = 'return',
['throws'] = 'error',
['typedef'] = 'type',
-- Typed aliases.
['bool'] = 'field',
['func'] = 'field',
['int'] = 'field',
['number'] = 'field',
['string'] = 'field',
['tab'] = 'field',
['vararg'] = 'param',
['tfield'] = 'field',
['tparam'] = 'param',
['treturn'] = 'return'
}
p.tags._type_alias = {
-- Implicit type value alias.
['bool'] = 'boolean',
['func'] = 'function',
['int'] = 'number',
['number'] = 'number',
['string'] = 'string',
['tab'] = 'table',
['vararg'] = '...',
-- Pure typed modifier alias.
['tfield'] = 'variable',
['tparam'] = 'variable',
['treturn'] = 'variable'
}
p.tags._project_level = {
-- Contains code.
['module'] = true,
['script'] = true,
['classmod'] = true,
['submodule'] = true,
['file'] = true,
-- Contains documentation.
['topic'] = true,
['example'] = true
}
p.tags._code_types = {
['module'] = true,
['script'] = true,
['classmod'] = true
}
p.tags._module_info = {
['image'] = true,
['caption'] = true,
['release'] = true,
['author'] = true,
['copyright'] = true,
['license'] = true,
['require'] = true,
['credit'] = true,
['attribution'] = true,
['demo'] = true
}
p.tags._annotation_tags = {
['field'] = true,
['warning'] = true,
['fixme'] = true,
['note'] = true,
['todo'] = true,
['see'] = true
}
p.tags._annotation_hierarchy = {
'warning',
'fixme',
'note',
'todo',
'see'
}
p.tags._privacy_tags = {
['private'] = true,
['local'] = true
}
p.tags._generic_tags = {
['variable'] = true,
['member'] = true
}
p.tags._subtype_tags = {
['factory'] = true,
['local'] = true,
['private'] = true,
['constructor'] = true,
['static'] = true
}
p.tags._subtype_hierarchy = {
'private',
'local',
'static',
'factory',
'constructor'
}
return p
97e0wti9mg3650r9kw1xqydjisw6dtc
803403
803402
2026-07-29T23:41:08Z
SM7
3953
1 revision imported from [[:en:Module:Docbunto]]
803402
Scribunto
text/plain
--- Docbunto is an automatic documentation generator for Scribunto modules.
-- The module is based on LuaDoc and LDoc. It produces documentation in
-- the form of MediaWiki markup, using `@tag`-prefixed comments embedded
-- in the source code of a Scribunto module. The taglet parser & doclet
-- renderer Docbunto uses are also publicly exposed to other modules.
--
-- Docbunto code items are introduced by a block comment (`--[[]]--`), an
-- inline comment with three hyphens (`---`), or an inline `@tag` comment.
-- The module can use static code analysis to infer variable names, item
-- privacy (`local` keyword), tables (`{}` constructor) and functions
-- (`function` keyword). MediaWiki and Markdown formatting is supported.
--
-- Items are usually rendered in the order they are defined, if they are
-- public items, or emulated classes extending the Lua primitives. There
-- are many customisation options available to change Docbunto behaviour.
--
-- @module docbunto
-- @alias p
-- @require Module:I18n
-- @require Module:Lua_lexer
-- @require Module:Unindent
-- @require Module:Yesno
-- @require Module:Arguments
-- @require Module:Documentation
-- @author [[wikia:dev:User:8nml|8nml]] (Fandom Dev Wiki)
-- @author [[User:Awesome Aasim|Awesome Aasim]] (Wikimedia port)
-- @attribution [https://github.com/stevedonovan @stevedonovan] ([https://github.com/stevedonovan/LDoc GitHub])
-- @release beta
-- <nowiki>
local p = {}
-- Module dependencies.
local title = mw.title.getCurrentTitle()
local i18n = require('Module:I18n').loadMessages('Docbunto'):useContentLang()
local references = mw.loadData('Module:Docbunto/references')
local lexer = require('Module:Lua lexer')
local unindent = require('Module:Unindent')
local yesno = require('Module:Yesno')
local doc = require('Module:Documentation')
local fileTooBig
local usingOptionsContent
local hasTodoItems
-- Module variables.
local DEFAULT_TITLE = title.namespace == 828 and doc.getEnvironment({}).templateTitle.text or ''
local DEFAULT_VARIABLE = DEFAULT_TITLE
:gsub('^%u', mw.ustring.lower)
:gsub('%s', '_'):gsub('%p', '.')
local frame, gsub, match
local syntaxhighlightPresent = mw.getCurrentFrame()
:preprocess("<syntaxhighlight lang=\"lua\">-- Lua</syntaxhighlight>")
~= "<syntaxhighlight lang=\"lua\">-- Lua</syntaxhighlight>"
-- Docbunto variables & tag tokens.
local TAG_MULTI = 'M'
local TAG_ID = 'ID'
local TAG_SINGLE = 'S'
local TAG_TYPE = 'T'
local TAG_FLAG = 'N'
local TAG_MULTI_LINE = 'ML'
-- Docbunto processing patterns.
local patterns = {}
--- Configuration options.
-- @table options
-- @field[opt] {boolean} options.all Include local items in
-- documentation.
-- @field[opt] {boolean} options.autodoc Whether the module is being
-- called to automatically provide missing documentation.
-- @field[opt] {boolean} options.boilerplate Removal of
-- boilerplate (license block comments).
-- @field[opt] {string} options.caption Infobox image caption.
-- @field[opt] {boolean} options.code Only document Docbunto code
-- items - exclude article infobox and lede from
-- rendered documentation. Permits article to be
-- edited in VisualEditor.
-- @field[opt] {boolean} options.colon Format tags with a `:` suffix
-- and without the `@` prefix. This bypasses the "doctag
-- soup" some authors complain of.
-- @field[opt] {string} options.image Infobox image.
-- @field[opt] {boolean} options.box Include infobox in output.
-- @field[opt] {boolean} options.footer Append a horizontal line &
-- "generated by Docbunto" to the bottom of the output.
-- @field[opt] {boolean} options.noluaref Don't link to the [[mw:
-- Extension:Scribunto/Lua reference manual|Lua reference manual]] for types.
-- @field[opt] {boolean} options.card Include endmatter card in the
-- output at the end of the documentation.
-- @field[opt] {boolean} options.plain Disable Markdown formatting
-- in documentation.
-- @field[opt] {string} options.preface Preface text to insert
-- between lede & item documentation, used to provide
-- usage and code examples.
-- @field[opt] {boolean} options.simple Limit documentation to
-- descriptions only. Removes documentation of
-- subitem tags such as `@param` and `@field` ([[#Item
-- subtags|see list]]).
-- @field[opt] {boolean} options.sort Sort documentation items in
-- alphabetical order.
-- @field[opt] {boolean} options.strip Remove table index in
-- documentation.
-- @field[opt] {boolean} options.ulist Indent subitems as `<ul>`
-- lists (LDoc/JSDoc behaviour).
-- @field[opt] {boolean} options.verbose Show the code
-- description of a package function when documenting
-- function modules.
-- Docbunto private utilities.
local utils = {}
--- @{string.find} optimisation for @{string} functions.
-- @function utils.strfind_wrap
-- @param {function} strfunc String library function.
-- @return {function} Function wrapped in @{string.find} check.
-- @local
function utils.strfind_wrap(func)
return function(...)
local arg = {...}
if string.find(arg[1], arg[2]) then
return func(...);
end
end
end
--- String concatenation utility.
-- @function join
-- @param {sequence<string>} tbl Table of string segments
-- @return {function} A concatenated string.
-- @local
function utils.join(tbl)
return table.concat(tbl, '')
end
--- Pattern configuration function.
-- Resets patterns for each documentation build.
-- @function utils.configure_patterns
-- @param {options} options Configuration options.
-- @local
function utils.configure_patterns(options)
-- Setup Unicode or ASCII character encoding (optimisation).
gsub = utils.strfind_wrap(
options.unicode
and mw.ustring.gsub
or string.gsub
)
match = utils.strfind_wrap(
options.unicode
and mw.ustring.match
or string.match
)
patterns.DOCBUNTO_SUMMARY =
options.iso639_th
and '^[^ ]+'
or
options.unicode
and '^[^.։。।෴۔።]+[.։。।෴۔።]?'
or '^[^.]+%.?'
patterns.DOCBUNTO_CONCAT = options.iso639_th and '' or ' '
patterns.DOCBUNTO_TYPE = '^{({*[^}]+}*)}%s*'
patterns.DOCBUNTO_WIKITEXT = '^[{|!}:#*=]+[%s-}]+'
-- Setup parsing tag patterns with colon mode support.
patterns.DOCBUNTO_TAG = options.colon and '^%s*(%w+):' or '^%s*@(%w+)'
patterns.DOCBUNTO_TAG_COMMENT = '^[-%s]*' .. patterns.DOCBUNTO_TAG:sub(2)
patterns.DOCBUNTO_TAG_VALUE = patterns.DOCBUNTO_TAG .. '(.*)'
patterns.DOCBUNTO_TAG_MOD_VALUE = patterns.DOCBUNTO_TAG .. '%[([^%]]*)%](.*)'
patterns.DOCBUNTO_TAG_OPTION_VALUE = '(%[[^%]]*%])%s*(.*)'
end
--- Tag processor function.
-- @function utils.process_tag
-- @param {string} str Tag string to process.
-- @return {table} Tag object.
-- @local
function utils.process_tag(str)
local tag = {}
if str:find(patterns.DOCBUNTO_TAG_MOD_VALUE) then
tag.name, tag.modifiers, tag.value = str:match(patterns.DOCBUNTO_TAG_MOD_VALUE)
local modifiers = {}
for m in tag.modifiers:gmatch('[^%s,]+') do
local i = m:find('=')
if i then
local key = mw.text.trim(m:sub(1, i - 1))
local val = mw.text.trim(m:sub(i + 1, #m))
modifiers[key] = val
else
modifiers[m] = true
end
end
if modifiers.optchain then
modifiers.opt = true
modifiers.optchain = nil
end
tag.modifiers = modifiers
else
tag.name, tag.value = str:match(patterns.DOCBUNTO_TAG_VALUE)
end
tag.value = mw.text.trim(tag.value)
if p.tags._type_alias[tag.name] then
if p.tags._type_alias[tag.name] ~= 'variable' then
tag.value = p.tags._type_alias[tag.name] .. ' ' .. tag.value
tag.name = 'field'
end
if tag.value:match('^%S+') ~= '...' then
tag.value = tag.value:gsub('^(%S+)', '{%1}')
end
end
tag.name = p.tags._alias[tag.name] or tag.name
if tag.name ~= 'usage' and tag.value:find(patterns.DOCBUNTO_TYPE) then
tag.type = tag.value:match(patterns.DOCBUNTO_TYPE)
if tag.type:find('^%?') then
tag.type = tag.type:sub(2) .. '|nil'
end
tag.value = tag.value:gsub(patterns.DOCBUNTO_TYPE, '')
end
if (tag.name == 'param' or tag.name == 'field') and tag.value:find(patterns.DOCBUNTO_TAG_OPTION_VALUE) then
tag.value = tag.value:gsub(patterns.DOCBUNTO_TAG_OPTION_VALUE, '%1 %2')
tag.modifiers = tag.modifiers or {}
tag.modifiers.opt = true
end
if p.tags[tag.name] == TAG_FLAG then
tag.value = true
end
return tag
end
--- Module info extraction utility.
-- @function utils.extract_info
-- @param {table} documentation Package doclet info.
-- @return {table} Information name-value map.
-- @local
function utils.extract_info(documentation)
local info = {}
for _, tag in ipairs(documentation.tags) do
if p.tags._module_info[tag.name] then
if info[tag.name] then
if not info[tag.name]:find('^%* ') then
info[tag.name] = '* ' .. info[tag.name]
end
info[tag.name] = info[tag.name] .. '\n* ' .. tag.value
else
info[tag.name] = tag.value
end
end
end
return info
end
--- Type extraction utility.
-- @function utils.extract_types
-- @param {table} item Item documentation data.
-- @return {sequence<string>} Item types.
-- @local
function utils.extract_types(item)
local item_types = {}
for _, tag in ipairs(item.tags) do
if p.tags[tag.name] == TAG_TYPE then
item_types[1] = tag.name
if tag.name == 'variable' then
local implied_local = utils.process_tag('@local')
table.insert(item.tags, mw.clone(implied_local))
item.tags['local'] = mw.clone(implied_local)
end
if
p.tags._generic_tags[item_types[1]] and
not p.tags._project_level[item_types[1]] and
tag.type
then
item_types[#item_types + 1] = tag.type
end
break
end
end
return item_types
end
--- Name extraction utility.
-- @function utils.extract_name
-- @param {table} item Item documentation data.
-- @param {boolean} project Whether the item is project-level.
-- @return {string} Item name.
-- @local
function utils.extract_name(item, opts)
opts = opts or {}
local item_name
for _, tag in ipairs(item.tags) do
if p.tags[tag.name] == TAG_TYPE then
item_name = tag.value; break
end
end
if item_name or not opts.project then
return item_name
end
item_name = item.code:match('\nreturn%s+([%w_]+)')
if item_name == 'p' and not item.tags['alias'] then
local implied_alias = { name = 'alias', value = 'p' }
item.tags['alias'] = implied_alias
table.insert(item.tags, implied_alias)
end
item_name = (item_name and item_name ~= 'p')
and item_name
or item.filename
:gsub('^' .. mw.site.namespaces[828].name .. ':', '')
:gsub('^(%u)', mw.ustring.lower)
:gsub('/', '.'):gsub(' ', '_')
return item_name
end
--- Source code utility for item name detection.
-- @function utils.deduce_name
-- @param {string} tokens Stream tokens for first line.
-- @param {string} index Stream token index.
-- @param {table} opts Configuration options.
-- @param[opt] {boolean} opts.lookahead Whether a variable name succeeds the index.
-- @param[opt] {boolean} opts.lookbehind Whether a variable name precedes the index.
-- @return {string} Item name.
-- @local
function utils.deduce_name(tokens, index, opts)
local name = ''
if opts.lookbehind then
for i2 = index, 1, -1 do
if tokens[i2].type ~= 'keyword' then
name = tokens[i2].data .. name
else
break
end
end
elseif opts.lookahead then
for i2 = index, #tokens do
if tokens[i2].type ~= 'keyword' and not tokens[i2].data:find('^%(') then
name = name .. tokens[i2].data
else
break
end
end
end
return name
end
--- Parses Lua item names to extract a hierarchy.
--- @function utils.parse_hierarchy
-- @param {string} item_name Documentation item name.
-- @return {sequence<string>} List of global keys to index the
-- variable reference.
-- @local
function utils.parse_hierarchy(item_name)
local hierarchy = {}
for _, lexeme in ipairs(lexer(item_name)) do
if lexeme.type == 'ident' or lexeme.type == 'string' or lexeme.type == 'number' then
hierarchy[#hierarchy + 1] = lexeme.text
end
end
return hierarchy
end
--- Code analysis utility.
-- @function utils.code_heuristic
-- @param {table} item Item documentation data.
-- @local
function utils.code_heuristic(item)
local is_section = item.tags['private'] or item.tags['local'] or item.type == 'type'
if item.name and item.type and is_section then
return
end
local tokens = lexer(item.code:match('^[^\n]*'))[1]
local t, i = tokens[1], 1
local item_name, item_type
local lexemes = {}
while t do
if t.type ~= 'whitespace' then
lexemes[#lexemes + 1] = tokens[i]
end
t, i = tokens[i + 1], i + 1
end
t, i = lexemes[1], 1
while t do
if t.data == '=' and not item.name then
item_name = utils.deduce_name(lexemes, i - 1, { lookbehind = true })
end
if t.data == 'function' and not item.name then
item_type = 'function'
if lexemes[i + 1] and lexemes[i + 1].data ~= '(' then
item_name = utils.deduce_name(lexemes, i + 1, { lookahead = true })
end
end
if t.data == '{' or t.data == '{}' and not item.type then
item_type = 'table'
end
if t.data == 'local' and not is_section then
local implied_local = utils.process_tag('@local')
table.insert(item.tags, implied_local)
item.tags['local'] = mw.clone(implied_local)
end
t, i = lexemes[i + 1], i + 1
end
item.name = item.name or item_name or ''
item.type = item.type or item_type
if #(item.types or {}) == 0 then
item.types[1] = item.type
end
end
--- Array hash map conversion utility.
-- @function utils.hash_map
-- @param {table} item Item documentation data array.
-- @return {table} Item documentation data map.
-- @local
function utils.hash_map(array)
local map = array
for _, element in ipairs(array) do
if map[element.name] and not map[element.name].name then
table.insert(map[element.name], mw.clone(element))
elseif map[element.name] and map[element.name].name then
map[element.name] = { map[element.name], mw.clone(element) }
else
map[element.name] = mw.clone(element)
end
end
return map
end
--- Item export utility.
-- @function utils.export_item
-- @param {table} documentation Package documentation data.
-- @param {string} item_reference Identifier name for item.
-- @param {string} item_index Identifier name for item.
-- @param {string} item_alias Export alias for item.
-- @param {boolean} factory_item Whether the documentation item is a factory function.
-- @local
function utils.export_item(documentation, item_reference, item_index, item_alias, factory_item)
for _, item in ipairs(documentation.items) do
if item_reference == item.name then
item.tags['local'] = nil
item.tags['private'] = nil
for index, tag in ipairs(item.tags) do
if p.tags._privacy_tags[tag.name] then
table.remove(item.tags, index)
end
end
item.type, item.types[1] = 'member', 'member'
local accessor
if item_alias:find('^%[') then
accessor = ''
else
local is_class = documentation.type == 'classmod' or factory_item
if is_class and not item.tags['static'] then
accessor = ':'
else
accessor = '.'
end
end
if factory_item then
item.alias = utils.join{
documentation.items[item_index].tags['factory'].value,
accessor,
item_alias
}
else
item.alias = utils.join{
((documentation.tags['alias'] or {}).value or documentation.name),
accessor,
item_alias
}
end
item.hierarchy = utils.parse_hierarchy(item.alias)
break
end
end
end
--- Subitem tag correction utility.
-- @function utils.correct_subitem_tag
-- @param {table} item Item documentation data.
-- @local
function utils.correct_subitem_tag(item)
local field_tag = item.tags['field']
if item.type ~= 'function' or not field_tag then
return
end
if field_tag.name then
field_tag.name = 'param'
else
for _, tag_el in ipairs(field_tag) do
tag_el.name = 'param'
end
end
local param_tag = item.tags['param']
if param_tag and not param_tag.name then
if field_tag.name then
table.insert(param_tag, field_tag)
else
for _, tag_el in ipairs(field_tag) do
table.insert(param_tag, tag_el)
end
end
elseif param_tag and param_tag.name then
if field_tag.name then
param_tag = { param_tag, field_tag }
else
for i, tag_el in ipairs(field_tag) do
if i == 1 then
param_tag = { param_tag }
end
for _, tag_el in ipairs(field_tag) do
table.insert(param_tag, tag_el)
end
end
end
else
param_tag = field_tag
end
item.tags['field'] = nil
end
--- Item override tag utility.
-- @function utils.override_item_tag
-- @param {table} item Item documentation data.
-- @param {string} name Tag name.
-- @param[opt] {string} alias Target alias for tag.
-- @local
function utils.override_item_tag(item, name, alias)
if item.tags[name] then
item[alias or name] = item.tags[name].value
end
end
--- Identity function.
-- @function utils.identity
-- @param x The variable by reference.
-- @return The parameter `x` by reference.
-- @local
function utils.identity(x)
return x
end
--- Argument processing
-- @param {string} funcName name of function
-- @return function to process arguments
-- @local
local function makeInvokeFunc(funcName)
return function (f)
local args = require("Module:Arguments").getArgs(f, {
valueFunc = function (key, value)
if type(value) == 'string' then
value = value:match('^%s*(.-)%s*$') -- Remove whitespace.
if key == 'heading' or value ~= '' then
return value
else
return nil
end
else
return value
end
end
})
return p[funcName](args)
end
end
-- Docbunto rendering logic.
local frontend = {}
--- Markdown header converter.
-- @function urils.markdown_header
-- @param {string} hash Leading hash.
-- @param {string} text Header text.
-- @return {string} MediaWiki header.
-- @local
function frontend.markdown_header(hash, text)
local symbol = '='
return utils.join{
'\n',
symbol:rep(#hash), ' ', text, ' ', symbol:rep(#hash),
'\n'
}
end
--- Item reference formatting.
-- @function urils.item_reference
-- @param {string} ref Item reference.
-- @return {string} Internal MediaWiki link to article item.
-- @local
function frontend.item_reference(ref)
local temp = mw.text.split(ref, '|')
local item = temp[1]
local text = temp[2] or temp[1]
if references.items[item] then
local interwiki = 'mw'
item = interwiki .. references.items[item]
else
item = '#' .. item
end
return mw.text.tag{
name = 'code',
content = utils.join{ '[[', item, '|', text, ']]' }
}
end
--- Doclet type reference preprocessor.
-- Formats types with links to the [[mw:Extension:Scribunto/Lua reference manual|Lua reference manual]].
-- @function frontend.type_reference
-- @param {table} item Item documentation data.
-- @param {options} options Configuration options.
-- @local
function frontend.type_reference(item, options)
local interwiki = 'mw:'
if
not options.noluaref and
item.value and
item.value:match('^%S+') == mw.text.tag('code', nil, '...')
then
item.value = item.value:gsub('^(%S+)', mw.text.tag{
name = 'code',
content = utils.join{ '[[', interwiki, 'Lua reference manual#varargs|...]]' }
})
end
-- Handle tag type references.
item.types = item.types or item.type and { item.type }
if not item.types then
return
end
for index, item_type in ipairs(item.types) do
item.types[index] = item.types[index]:gsub('[%w_. ]+', function(item_type)
local data = references.types[item_type]
local name = data and data.name or item_type
if not name:match('%.') and not name:match('^%u') and data then
name = i18n:msg('type-' .. name)
end
if data and not options.noluaref then
return utils.join{ '[[', interwiki, data.link, '|', name , ']]' }
elseif
item_type:find(i18n:msg('line-no'):gsub('$1', ''))
then
return utils.join { '[[' , options.file , '#L-', item_type:sub(6), '|', name, ']]' }
elseif
not options.noluaref and
not p.tags._generic_tags[item_type]
then
return utils.join { '[[#', item_type, '|', name, ']]' }
end
end)
end
item.type = table.concat(item.types, i18n:msg('separator-dot'))
end
--- Markdown preprocessor to MediaWiki format.
-- @function frontend.markdown
-- @param {string} str Unprocessed Markdown string.
-- @return {string} MediaWiki-compatible markup with HTML formatting.
-- @local
function frontend.markdown(str)
-- Bold & italic tags.
str = str:gsub('%*%*%*([^\n*]+)%*%*%*', '<b><i>%1</i></b>')
str = str:gsub('%*%*([^\n*]+)%*%*', '<b>%1</b>')
str = str:gsub('%*([^\n*]+)%*', '<i>%1</i>')
-- Self-closing header support.
str = str:gsub('%f[^\n%z](#+) *([^\n#]+) *#+%s', frontend.markdown_header)
-- External and internal links.
str = str:gsub('%[([^\n%]]+)%]%(([^\n][^\n)]-)%)', '[%2 %1]')
str = str:gsub('%{@link%s+([^}]+)%}', '[%1]')
str = str:gsub('%@{([^\n}]+)}', frontend.item_reference)
-- Programming & scientific notation.
str = str:gsub('%f["`]`([^\n`]+)`%f[^"`]', '<code><nowiki>%1</nowiki></code>')
str = str:gsub('%$%$([^\n$]+)%$%$', '<math display="inline">%1</math>')
-- Strikethroughs.
str = str:gsub('~~([^\n~]+)~~', '<del>%1</del>')
-- HTML output.
return str
end
--- Doclet function item preprocessor.
-- Formats item name as a function call with top-level arguments.
-- @function frontend.pretask_function_name
-- @param {table} item Item documentation data.
-- @param {options} options Configuration options.
-- @local
function frontend.pretask_function_name(item, options)
local target = item.alias and 'alias' or 'name'
item[target] = item[target] .. '('
if
item.tags['param'] and
item.tags['param'].value and
not item.tags['param'].value:find('^[%w_]+[.[]')
then
if (item.tags['param'].modifiers or {})['opt'] then
local optional = mw.html.create('span')
optional:css('opacity', '0.65')
optional:wikitext(item.tags['param'].value:match('^(%S+)'))
item[target] = item[target] .. tostring(optional)
else
item[target] = item[target] .. item.tags['param'].value:match('^(%S*)')
end
elseif item.tags['param'] then
for index, tag in ipairs(item.tags['param']) do
if not tag.value:find('^[%w_]+[.[]') then
if (tag.modifiers or {})['opt'] then
local param = mw.html.create('span')
param:css('opacity', '0.65')
param:wikitext((index > 1 and ', ' or '') .. tag.value:match('^%S+'))
item[target] = item[target] .. tostring(param)
else
item[target] = item[target] .. (index > 1 and ', ' or '') .. tag.value:match('^(%S+)')
end
end
end
end
item[target] = item[target] .. ')'
end
--- Doclet parameter/field subitem preprocessor.
-- Indents and wraps variable prefix with `code` tag.
-- @function pretask_variable_prefix
-- @param {table} item Item documentation data.
-- @param {options} options Configuration options.
-- @local
function frontend.pretask_variable_prefix(item, options)
local indent_symbol = options.ulist and '*' or ':'
local indent_level, indentation, item_id
if item.value then
indent_level = item.value:match('^%S*') == '...'
and 0
or select(2, item.value:match('^%S*'):gsub('[.[]', ''))
indentation = indent_symbol:rep(indent_level)
item_id = options.item_id
item.value = indentation .. item.value:gsub(
'^(%S+)',
mw.text.tag{
name = 'code',
attrs = { id = item_id .. '~%1' },
content = '%1'
},
1
)
elseif item then
for _, item_el in ipairs(item) do
frontend.pretask_variable_prefix(item_el, options)
end
end
end
--- Doclet usage subitem preprocessor.
-- Formats usage example with `<syntaxhighlight>` tag.
-- @function frontend.pretask_usage_highlight
-- @param {table} item Item documentation data.
-- @param {options} options Configuration options.
-- @local
function frontend.pretask_usage_highlight(item, options)
if item.value then
item.value = unindent(mw.text.trim(item.value))
if item.value:find('^{{.+}}$') then
item.value = item.value:gsub('=', mw.text.nowiki)
local multi_line = item.value:find('\n') and '|m = 1|' or '|'
if item.value:match('^{{([^:]+)') == '#invoke' then
item.value = item.value:gsub('^{{#invoke:', '{{mlx' .. multi_line)
else
if options.entrypoint then
item.value = item.value:gsub('^([^|]+)|%s*([^|}]-)(%s*)([|}])','%1|"%2"%3%4')
end
item.value = item.value:gsub('^{{', '{{tlx' .. multi_line)
end
local highlight_class = tonumber(mw.site.currentVersion:match('^%d%.%d+')) > 1.19
and 'mw-highlight'
or 'mw-geshi'
if item.value:find('\n') then
local highlight_div = mw.html.create('div')
highlight_div.addClass(highlight_class)
highlight_div.addClass('mw-content-ltr')
highlight_div:attr('dir', 'ltr')
highlight_div:wikitext(item.value)
item.value = tostring(highlight_div)
else
item.value = mw.text.tag{
name = 'span',
attrs = { class = 'code' },
content = item.value
}
end
else
local highlight_tag = mw.html.create('syntaxhighlight')
highlight_tag:attr('lang', 'lua')
if not item.value:find('\n') then
highlight_tag:attr('inline', 'inline')
end
highlight_tag:wikitext(item.value)
item.value = tostring(highlight_tag)
end
elseif item then
for _, item_el in ipairs(item) do
frontend.pretask_usage_highlight(item_el, options)
end
end
end
--- Doclet error subitem preprocessor.
-- Formats line numbers (`{#}`) in error tag values.
-- @function frontend.pretask_error_line
-- @param {table} item Item documentation data.
-- @local
function frontend.pretask_error_line(item, options)
if item.name then
local line
if item.modifiers and item.modifiers.line then
line = item.modifiers.line
end
for mod in pairs(item.modifiers or {}) do
if mod:find('^%d+$') then line = mod end
end
if line then
if item.types then
item.types[#item.types + 1] = i18n:msg('line-no', line)
else
item.type = i18n:msg('line-no', line)
end
end
elseif item then
for _, item_el in ipairs(item) do
frontend.pretask_error_line(item_el, options)
end
end
end
--- Doclet item renderer.
-- @function frontend.render_item
-- @param {table} stream Wikitext documentation stream.
-- @param {table} item Item documentation data.
-- @param {options} options Configuration options.
-- @param[opt] {function} pretask Item data preprocessor.
-- @local
function frontend.render_item(stream, item, options, pretask)
if pretask then pretask(item, options) end
local item_name = item.alias or item.name
frontend.type_reference(item, options)
local item_type = item.type
for _, name in ipairs(p.tags._subtype_hierarchy) do
if item.tags[name] then
item_type = item_type .. i18n:msg('separator-dot') .. name
end
end
item_type = i18n:msg('parentheses', item_type)
if options.strip and item.export and item.hierarchy then
item_name = item_name:gsub('^[%w_]+[.[]?', '')
end
-- add single spaces between parentheses similar to
-- MediaWiki documentation
if not item_name:find( '()', 1, true ) then
item_name = item_name:gsub('%(', '( ')
item_name = item_name:gsub('%)', ' )')
end
item_name = item_name:gsub('%[', '[ ')
item_name = item_name:gsub('%]', ' ]')
local code_node = mw.html.create('code')
:attr('id', options.item_id)
:wikitext(item_name)
if not usingOptionsContent then
local span_node = stream:wikitext(';')
:tag('span')
:addClass('plainlinks docbunto-link')
if fileTooBig or not syntaxhighlightPresent then
span_node:wikitext('[{{fullurl:' .. options.file .. '|action=edit#mw-ce-l' .. item.lineno .. '}} ' .. tostring(code_node) .. ']')
elseif title.namespace == 828 and options.file == title.text then
span_node:wikitext('[[' .. '#L-' .. item.lineno .. '|' .. tostring(code_node) .. ']]')
else
span_node:wikitext('[[' .. options.file .. '#L-' .. item.lineno .. '|' .. tostring(code_node) .. ']]')
end
else
stream:wikitext(';' .. tostring(code_node))
end
stream
:wikitext(item_type)
:newline()
if (#(item.summary or '') + #item.description) ~= 0 then
local separator = #(item.summary or '') ~= 0 and #item.description ~= 0
and (item.description:find(patterns.DOCBUNTO_WIKITEXT) and '\n' or patterns.DOCBUNTO_CONCAT)
or ''
local intro = (item.summary or '') .. separator .. item.description
intro = intro
:gsub('\n([{:#*])', '\n:%1')
:gsub('\n\n([^=])', '\n:%1')
stream:wikitext(':' .. intro):newline()
end
end
--- Doclet tag renderer.
-- @function frontend.render_tag
-- @param {table} stream Wikitext documentation stream.
-- @param {string} name Item tag name.
-- @param {table} tag Item tag data.
-- @param {options} options Configuration options.
-- @param[opt] {function} pretask Item data preprocessor.
-- @local
function frontend.render_tag(stream, name, tag, options, pretask)
if pretask then pretask(tag, options) end
if tag.value then
frontend.type_reference(tag, options)
local tag_name = i18n:msg('tag-' .. name, '1')
-- Handle ul/ol/dl comment support.
stream:wikitext(':')
:tag('b')
:wikitext(tag_name)
:done():done()
:wikitext(i18n:msg('separator-semicolon'))
:wikitext((mw.text.trim(tag.value):gsub('\n([{:#*])', '\n:%1')))
if tag.value:find('\n[{:#*]') and (tag.type or (tag.modifiers or {})['opt']) then
stream:newline():wikitext(':')
end
-- Type and optional appositive for tag.
if tag.type and (tag.modifiers or {})['opt'] then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = {
tag.type ..
i18n:msg('separator-colon') ..
i18n:msg('optional')
}
})
elseif tag.type then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = { tag.type }
})
elseif (tag.modifiers or {})['opt'] then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = { i18n:msg('optional') }
})
end
stream:newline()
else
local tag_name = i18n:msg('tag-' .. name, tostring(#tag))
stream
:wikitext(':')
:tag('b')
:wikitext(tag_name)
:done():done()
:wikitext(i18n:msg('separator-semicolon'))
:newline()
for _, tag_el in ipairs(tag) do
frontend.type_reference(tag_el, options)
local marker = options.ulist and '*' or ':'
stream:wikitext(':' .. marker .. tag_el.value:gsub('\n([{:#*])', '\n:' .. marker .. '%1'))
if tag_el.value:find('\n[{:#*]') and (tag_el.type or (tag_el.modifiers or {})['opt']) then
stream:newline():wikitext(':' .. marker .. (tag_el.value:match('^[*:]+') or ''))
end
if tag_el.type and (tag_el.modifiers or {})['opt'] then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = {
tag_el.type ..
i18n:msg('separator-colon') ..
i18n:msg('optional')
}
})
elseif tag_el.type then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = { tag_el.type }
})
elseif (tag_el.modifiers or {})['opt'] then
stream:wikitext(i18n:msg{
key = 'parentheses',
args = { i18n:msg('optional') }
})
end
stream:newline()
end
end
end
--- Template entrypoint for [[Template:Docbunto]].
-- @function p._main
-- @param {table} args Arguments
-- @return {string} Module documentation output.
function p._main(args)
frame = mw.getCurrentFrame()
local modname = mw.text.trim(args[1] or args.file or DEFAULT_TITLE)
local options = {}
options.file = modname
options.all = yesno(args.all or false)
options.autodoc = yesno(args.autodoc or false)
options.boilerplate = yesno(args.boilerplate or options.autodoc or false)
options.box = yesno(args.box or args.infobox or options.autodoc or false)
options.caption = args.caption
options.card = yesno(args.card or (title.namespace == 828 and not options.autodoc))
options.code = yesno(args.code or false)
options.colon = yesno(args.colon or false)
options.footer = yesno(args.footer or title.namespace == 828)
options.image = args.image
options.migration = yesno(args.migration or false)
options.noluaref = yesno(args.noluaref or false)
options.plain = yesno(args.plain or false)
options.preface = args.preface
options.simple = yesno(args.simple or false)
options.sort = yesno(args.sort or false)
options.strip = yesno(args.strip or false)
options.verbose = yesno(args.verbose or false)
options.ulist = yesno(args.ulist or false)
return p.build(modname, options)
end
--- Entrypoint for the module.
-- @function p.main
-- @param {Frame} frame Module frame.
-- @return {string} Module documentation output.
p.main = makeInvokeFunc('_main')
--- Scribunto documentation generator entrypoint.
-- @function p.build
-- @param[opt] {string} modname Module page name (without namespace).
-- Default: second-level subpage.
-- @param[opt] {options} options Configuration options.
function p.build(modname, options)
modname = modname or DEFAULT_TITLE
options = options or {}
local tagdata = p.taglet(modname, options)
local docdata = p.doclet(tagdata, options)
return docdata
end
--- Docbunto taglet parser for Scribunto modules.
-- @function p.taglet
-- @param[opt] {string} modname Module page name (without namespace).
-- @param[opt] {options} options Configuration options.
-- @error[line=1081] {string} 'Lua source code not found in $1'
-- @error[line=1087] {string} 'documentation markup for Docbunto not found in $1'
-- @return {table} Module documentation data.
function p.taglet(modname, options)
modname = modname or DEFAULT_TITLE
options = options or {}
local filepath = mw.site.namespaces[828].name .. ':' .. modname
local content = mw.title.new(filepath):getContent()
-- Content checks.
if options.content then
content = options.content
usingOptionsContent = true
else
content = mw.title.new(filepath):getContent() or error(i18n:msg('no-content', filepath), 0)
end
-- Syntax highlighting is disabled for files above a specific size
fileTooBig = content:len() > 102400
if
not content:match('%-%-%-') and
not content:match(options.colon and '%w+:' or '@%w+')
then
error(i18n:msg('no-markup', filepath))
end
-- Remove leading escapes.
content = content:gsub('^%-%-+%s*<[^>]+>\n', '')
-- Remove closing pretty comments.
content = content:gsub('\n%-%-%-%-%-+(\n[^-]+)', '\n-- %1')
-- Remove boilerplate block comments.
if options.boilerplate then
content = content:gsub('^%-%-%[=*%[\n.-\n%-?%-?%]%=*]%-?%-?%s+', '')
content = content:gsub('%s+%-%-%[=*%[\n.-\n%-?%-?%]%=*]%-?%-?$', '')
end
-- Configure patterns for colon mode and Unicode character encoding.
options.unicode = type(content:find('[^%w%c%p%s]+')) == 'number'
options.iso639_th = type(content:find('\224\184[\129-\155]')) == 'number'
utils.configure_patterns(options)
-- Content lexing.
local lines = lexer(content)
local tokens = {}
local dummy_token = {
data = '',
posFirst = 1,
posLast = 1
}
local token_closure = 0
for _, line in ipairs(lines) do
if #line == 0 then
dummy_token.type = token_closure == 0
and 'whitespace'
or tokens[#tokens].type
table.insert(tokens, mw.clone(dummy_token))
else
for _, token in ipairs(line) do
if token.data:find('^%[=*%[$') or token.data:find('^%-%-%[=*%[$') then
token_closure = 1
end
if token.data:find(']=*]') then
token_closure = 0
end
table.insert(tokens, token)
end
end
end
-- Start documentation data.
local documentation = {}
documentation.filename = filepath
documentation.description = ''
documentation.code = content
documentation.comments = {}
documentation.tags = {}
documentation.items = {}
local line_no = 0
local item_index = 0
-- Taglet tracking variables.
local start_mode = true
local comment_mode = false
local doctag_mode = false
local export_mode = false
local special_tag = false
local pragma_mode = false
local factory_mode = false
local return_mode = false
local comment_tail = ''
local tag_name = ''
local new_item = false
local new_tag = false
local new_tag_item = false
local new_item_code = false
local pretty_comment = false
local comment_brace = false
local t, i, item = tokens[1], 1
local p_success, p_error = pcall(function()
while t do
-- Taglet variable update.
new_item = t.data:find('^%-%-%-') or t.data:find('^%-%-%[%=*%[$')
comment_tail = t.data:gsub('^%-%-+', '')
tag_name = comment_tail:match(patterns.DOCBUNTO_TAG)
tag_name = p.tags._alias[tag_name] or tag_name
new_tag = type(p.tags[tag_name]) == 'string'
local last_token = t.posFirst ~= 1 and 2 or 1
new_tag_item = (
new_tag and i > 1 and tokens[i - last_token].type ~= 'comment'
and not tokens[i - last_token].data:find(patterns.DOCBUNTO_TAG_COMMENT)
)
pretty_comment =
t.data:find('^%-%-%-+%s*$') or
t.data:find('[^-]+%-%-%-+%s*$') or
t.data:find('%-*[ \t]*<nowiki>$') or
t.data:find('%-*[ \t]*<pre>$')
comment_brace =
t.data:find('^%-%-%[=*%[$') or
t.data:find('^%-%-%]=*%]$') or
t.data:find('^%]=*%]%-%-$')
pragma_mode = tag_name == 'pragma'
export_mode = tag_name == 'export' and not doctag_mode
special_tag = pragma_mode or export_mode
local tags, tag, subtokens, separator
-- Line counter.
if t.posFirst == 1 then
line_no = line_no + 1
end
-- Data insertion logic.
if t.type == 'comment' then
if new_item then
comment_mode = true
end
if (new_tag or new_tag_item) and not special_tag then
comment_mode = true
doctag_mode = true
end
-- Module-level documentation taglet.
if start_mode then
table.insert(documentation.comments, t.data)
end
-- Module description.
if
start_mode and comment_mode and
not new_tag and not doctag_mode and
not pretty_comment and not comment_brace
then
separator = mw.text.trim(comment_tail):find(patterns.DOCBUNTO_WIKITEXT)
and '\n'
or (#documentation.description ~= 0 and patterns.DOCBUNTO_CONCAT or '')
documentation.description = utils.join{
documentation.description,
separator,
mw.text.trim(comment_tail)
}
end
-- Switch from module description to module-level tag list.
if start_mode and new_tag and not export_mode then
doctag_mode = true
table.insert(documentation.tags, utils.process_tag(comment_tail))
-- Concatenating module-level tag with more information.
elseif
start_mode and doctag_mode and
not pretty_comment and not comment_brace
then
tags = documentation.tags
if p.tags[tags[#tags].name] == TAG_MULTI then
separator = mw.text.trim(comment_tail):find(patterns.DOCBUNTO_WIKITEXT)
and '\n'
or patterns.DOCBUNTO_CONCAT
tags[#tags].value = utils.join{
tags[#tags].value,
separator,
mw.text.trim(comment_tail)
}
elseif p.tags[tags[#tags].name] == TAG_MULTI_LINE then
tags[#tags].value = utils.join{
tags[#tags].value,
'\n',
comment_tail
}
end
end
-- Detect a new documentation item.
if not start_mode and (new_item or new_tag_item) and not special_tag then
table.insert(documentation.items, {})
item_index = item_index + 1
item = documentation.items[item_index]
item.lineno = line_no
item.code = ''
item.comments = {}
item.description = ''
item.tags = {}
end
-- Concatenate an item with more information.
if
not start_mode and comment_mode and
not new_tag and not doctag_mode and
not pretty_comment and not comment_brace
then
separator = (mw.text.trim(comment_tail):find(patterns.DOCBUNTO_WIKITEXT))
and '\n'
or (#item.description ~= 0 and patterns.DOCBUNTO_CONCAT or '')
item.description = utils.join{
item.description,
separator,
mw.text.trim(comment_tail)
}
end
-- Switch from item information to item tags.
if not start_mode and new_tag and not special_tag then
doctag_mode = true
table.insert(item.tags, utils.process_tag(comment_tail))
-- Extend item tags with lines of further information.
elseif
not start_mode and doctag_mode and
not pretty_comment and not comment_brace
then
tags = item.tags
if p.tags[tags[#tags].name] == TAG_MULTI then
separator = mw.text.trim(comment_tail):find(patterns.DOCBUNTO_WIKITEXT)
and '\n'
or patterns.DOCBUNTO_CONCAT
tags[#tags].value = utils.join{
tags[#tags].value,
separator,
mw.text.trim(comment_tail)
}
elseif p.tags[tags[#tags].name] == TAG_MULTI_LINE then
tags[#tags].value = utils.join{
tags[#tags].value,
'\n',
comment_tail
}
end
end
-- Save pre-item comments to item information.
if not start_mode and (comment_mode or doctag_mode) then
table.insert(item.comments, t.data)
end
-- Export tag support.
if export_mode then
factory_mode = t.posFirst ~= 1
if factory_mode then
item.exports = true
else
documentation.exports = true
end
-- Parse export token data while concatenating code.
subtokens = {}
while t and (not factory_mode or (factory_mode and t.data ~= 'end')) do
if factory_mode then
item.code = utils.join{
item.code,
(t.posFirst == 1 and '\n' or ''),
t.data
}
end
-- Fetch our item export tokens aka "subtokens".
t, i = tokens[i + 1], i + 1
if t and t.posFirst == 1 then
line_no = line_no + 1
end
if t and t.type ~= 'whitespace' and t.type ~= 'keyword' and t.type ~= 'comment' then
table.insert(subtokens, t)
end
end
-- Tracking and boolean variables for parsing exports.
local separator = { [','] = true, [';'] = true }
local brace = { ['{'] = true, ['}'] = true }
local item_reference, item_alias = '', ''
local sequence_index, has_key = 0, false
local subtoken, index = subtokens[2], 2
-- Logic that exports items to an item or module.
while not brace[subtoken.data] do
if subtoken.data == '=' then
has_key = true
elseif not separator[subtoken.data] then
if has_key then
item_reference = item_reference .. subtoken.data
else
item_alias = item_alias .. subtoken.data
end
end
if subtokens[index + 1] and separator[subtokens[index + 1].data] or brace[subtokens[index + 1].data] then
if not has_key then
sequence_index = sequence_index + 1
item_reference, item_alias = item_alias, item_reference
item_alias = '[' .. tostring(sequence_index) .. ']'
end
utils.export_item(documentation, item_reference, item_index, item_alias, factory_mode)
item_reference, item_alias, has_key = '', '', false
end
subtoken, index = subtokens[index + 1], index + 1
end
if not factory_mode then
break -- end of file
else
factory_mode = false -- end of item
end
end
-- Pragma tag support.
if pragma_mode then
tag = utils.process_tag(comment_tail)
if tag.modifiers then
options[tag.value] = yesno((next((tag ).modifiers or {})), true)
elseif tag.value:find(' ') then
local index = tag.value:find(' ')
local key = tag.value:sub(1, index - 1)
local val = tag.value:sub(index + 1)
options[key] = yesno(val)
elseif options[tag.value] == nil then
options[tag.value] = true
end
end
end
-- Package data post-processing.
if (t.type ~= 'comment' or t.data:find('^%-%-%]=*%]$') or t.data:find('^%]=*%]%-%-$')) and (comment_mode or doctag_mode) and start_mode then
documentation.tags = utils.hash_map(documentation.tags)
documentation.name = utils.extract_name(documentation, { project = true })
documentation.info = utils.extract_info(documentation)
documentation.types = utils.extract_types(documentation) or { 'module' }
documentation.type = documentation.types[1]
-- N.B: summary field = 1st comment sentence; description = text remainder.
if #documentation.description ~= 0 then
documentation.summary = match(documentation.description, patterns.DOCBUNTO_SUMMARY)
documentation.description = documentation.description:sub(select(2, documentation.description:find('%s*', #documentation.summary + 1)) + 1)
end
documentation.description = documentation.description:gsub('%s%s+', '\n\n')
documentation.executable = p.tags._code_types[documentation.type] and true or false
utils.correct_subitem_tag(documentation)
utils.override_item_tag(documentation, 'name')
utils.override_item_tag(documentation, 'alias')
utils.override_item_tag(documentation, 'summary')
utils.override_item_tag(documentation, 'description')
utils.override_item_tag(documentation, 'class', 'type')
end
-- Item data post-processing.
if
(t.type ~= 'comment' or t.data:find('^%-%-%]=*%]$') or t.data:find('^%]=*%]%-%-$'))
and (comment_mode or doctag_mode) and item_index ~= 0
then
item.tags = utils.hash_map(item.tags)
item.name = utils.extract_name(item)
item.types = utils.extract_types(item)
item.type = item.types[1]
if #item.description ~= 0 then
item.summary = match(item.description, patterns.DOCBUNTO_SUMMARY)
item.description = item.description:sub(select(2, item.description:find('%s*', #item.summary + 1)) + 1)
end
item.description = item.description:gsub('%s%s+', '\n\n')
new_item_code = true
end
-- Documentation block reset.
if t.type ~= 'comment' or t.data:find('^%-%-%]=*%]$') or t.data:find('^%]=*%]%-%-$') then
start_mode = false
comment_mode = false
doctag_mode = false
export_mode = false
pragma_mode = false
end
-- Don't concatenate module return value into item code.
if t.data == 'return' and t.posFirst == 1 then
return_mode = true
end
-- Item code concatenation.
if item_index ~= 0 and not doctag_mode and not comment_mode and not comment_brace and not return_mode then
separator = #item.code ~= 0 and t.posFirst == 1 and '\n' or ''
item.code = utils.join{ item.code, separator, t.data }
-- Code analysis on item headline.
if new_item_code and item.code:find('\n') and t.posFirst == 1 then
utils.code_heuristic(item)
new_item_code = false
end
end
t, i = tokens[i + 1], i + 1
end
documentation.lineno = line_no
local package_name = (documentation.tags['alias'] or {}).value or documentation.name or DEFAULT_VARIABLE
local package_alias = (documentation.tags['alias'] or {}).value or 'p'
local export_ptn = '^%s([.[])'
for _, item in ipairs(documentation.items) do
if item.name == package_alias or (item.name and item.name:match('^' .. package_alias .. '[.[]')) then
item.alias = item.name:gsub(export_ptn:format(package_alias), documentation.name .. '%1')
end
if
item.name == package_name or
item.tags['export'] or
(item.name and package_name and item.name:find(export_ptn:format(package_name))) or
(item.alias and package_name and item.alias:find(export_ptn:format(package_name)))
then
item.export = true
item.tags['local'] = nil
item.tags['private'] = nil
for index, tag in ipairs(item.tags) do
if tag.name == 'local' or tag.name == 'private' then
table.remove(item.tags, index)
end
end
end
if item.name and (item.name:find('[.:]') or item.name:find('%[[\'"]')) then
item.hierarchy = utils.parse_hierarchy(item.name)
end
item.type = item.type or ((item.alias or item.name or ''):find('[.[]') and 'member' or 'variable')
utils.correct_subitem_tag(item)
utils.override_item_tag(item, 'name')
utils.override_item_tag(item, 'alias')
utils.override_item_tag(item, 'summary')
utils.override_item_tag(item, 'description')
utils.override_item_tag(item, 'class', 'type')
end
-- Sort documentation items by their access level.
-- Items are sorted by whether they are exported at the module level.
-- Items are then subsorted by whether the item is `@local` aka `@private`.
table.sort(documentation.items, function(item1, item2)
local inaccessible1 = item1.tags['local'] or item1.tags['private']
local inaccessible2 = item2.tags['local'] or item2.tags['private']
-- Send package items to the top.
if item1.export and not item2.export then
return true
elseif item2.export and not item1.export then
return false
-- Send private items to the bottom.
elseif inaccessible1 and not inaccessible2 then
return false
elseif inaccessible2 and not inaccessible1 then
return true
-- Optional alphabetical sort.
elseif options.sort then
return (item1.alias or item1.name) < (item2.alias or item2.name)
-- Sort via source code order by default.
else
return item1.lineno < item2.lineno
end
end)
end)
if not p_success then
mw.addWarning("Docbunto error: " .. p_error)
end
return documentation
end
--- Doclet renderer for Docbunto taglet data.
-- @function p.doclet
-- @param {table} data Taglet documentation data.
-- @param[opt] {options} options Configuration options.
-- @return {string} Wikitext documentation output.
function p.doclet(data, options)
local documentation = mw.html.create()
local namespace_ptn = utils.join{ '^', mw.site.namespaces[828].name, ':' }
local codepage = data.filename:gsub(namespace_ptn, '', 1)
options = options or {}
options.file = data.filename
frame = frame or mw.getCurrentFrame():getParent() or mw.getCurrentFrame()
local maybe_md = options.plain and utils.identity or frontend.markdown
local tohtml = function(str)
return frame:preprocess(maybe_md(str))
end
-- Disable edit sections for automatic documentation pages.
if not options.code then
documentation:wikitext(frame:preprocess('__NOEDITSECTION__'))
end
-- Information
if not options.code and options.box then
local custom, infobox = pcall(require, 'Module:Docbunto/infobox')
if custom and type(infobox) == 'function' then
documentation:wikitext(infobox(data, codepage, frame, options, title, maybe_md)):newline()
end
end
-- Documentation lede.
if not options.code and (#(data.summary or '') + #data.description) ~= 0 then
local separator = #(data.summary or '') ~= 0 and #data.description ~= 0
and (data.description:find(patterns.DOCBUNTO_WIKITEXT) and '\n\n' or patterns.DOCBUNTO_CONCAT)
or ''
local intro = utils.join{ data.summary or '', separator, data.description }
intro = tohtml(intro:gsub('^(' .. codepage .. ')', mw.text.tag('b', nil, '%1')))
if options.migration then
documentation:wikitext(frame:preprocess(mw.text.tag{
name = 'syntaxhighlight',
content = intro
})):newline():newline()
else
documentation:wikitext(intro):newline():newline()
end
end
-- Custom documentation preface.
if options.preface then
documentation:wikitext(options.preface):newline():newline()
end
-- Start code documentation.
local codedoc = mw.html.create()
local function_module = data.tags['param'] or data.tags['return']
local header_type =
documentation.type == 'classmod'
and 'class'
or function_module
and 'function'
or 'items'
if (function_module or #data.items ~= 0) and not options.code or options.preface then
codedoc
:tag('h2')
:wikitext(i18n:msg('header-documentation'))
:done()
:newline()
end
if (function_module or #data.items ~= 0) then
codedoc
:tag('h3')
:wikitext(i18n:msg('header-' .. header_type))
:done()
:newline()
end
-- Function module support.
if function_module then
data.type, data.types[1] = 'function', 'function'
options.item_id = data.name
if not options.code or not options.verbose then data.description = '' end
frontend.render_item(codedoc, data, options, frontend.pretask_function_name)
if not options.simple and data.tags['param'] then
frontend.render_tag(codedoc, 'param', data.tags['param'], options, frontend.pretask_variable_prefix)
end
if not options.simple and data.tags['error'] then
frontend.render_tag(codedoc, 'error', data.tags['error'], options, frontend.pretask_error_line)
end
if not options.simple and data.tags['return'] then
frontend.render_tag(codedoc, 'return', data.tags['return'], options)
end
end
-- Render documentation items.
local other_header = false
local private_header = false
local inaccessible
for _, item in ipairs(data.items) do
inaccessible = item.tags['local'] or item.tags['private']
if not options.all and inaccessible then
break
end
if
not other_header and item.type ~= 'section' and item.type ~= 'type' and
not item.export and not item.hierarchy and not inaccessible
then
codedoc
:tag('h3')
:wikitext(i18n:msg('header-other'))
:done()
:newline()
other_header = true
end
if not private_header and options.all and inaccessible then
codedoc
:tag('h3')
:wikitext(i18n:msg('header-private'))
:done()
:newline()
private_header = true
end
if item.type == 'section' then
codedoc
:tag('h3')
:wikitext((mw.ustring.gsub(item.summary or item.alias or item.name, '[.։。।෴۔።]$', '')))
:done()
:newline()
if #item.description ~= 0 then
codedoc:wikitext(mw.text.trim(item.description)):newline()
end
elseif item.type == 'type' then
if options.strip and item.export and item.hierarchy then
if item.alias then
item.alias = item.alias:gsub('^[%w_]+[.[]?', '')
else
item.name = item.name:gsub('^[%w_]+[.[]?', '')
end
end
codedoc
:tag('h3')
:wikitext(mw.text.tag('code', {}, item.alias or item.name))
:done()
:newline()
if (#(item.summary or '') + #item.description) ~= 0 then
local separator = #(item.summary or '') ~= 0 and #item.description ~= 0
and (item.description:find(patterns.DOCBUNTO_WIKITEXT) and '\n\n' or patterns.DOCBUNTO_CONCAT)
or ''
codedoc:wikitext((item.summary or '') .. separator .. item.description):newline()
end
elseif item.type == 'function' then
options.item_id = item.alias or item.name
frontend.render_item(codedoc, item, options, frontend.pretask_function_name)
if not options.simple and item.tags['param'] then
frontend.render_tag(codedoc, 'param', item.tags['param'], options, frontend.pretask_variable_prefix)
end
if not options.simple and item.tags['error'] then
frontend.render_tag(codedoc, 'error', item.tags['error'], options, frontend.pretask_error_line)
end
if not options.simple and item.tags['return'] then
frontend.render_tag(codedoc, 'return', item.tags['return'], options)
end
elseif
item.type == 'table' or
item.type ~= nil and (
item.type == 'member' or
item.type == 'variable'
) and (item.alias or item.name)
then
options.item_id = item.alias or item.name
frontend.render_item(codedoc, item, options)
if not options.simple and item.tags['field'] then
frontend.render_tag(codedoc, 'field', item.tags['field'], options, frontend.pretask_variable_prefix)
end
end
if item.type ~= 'section' and item.type ~= 'type' then
if not options.simple and item.tags['note'] then
frontend.render_tag(codedoc, 'note', item.tags['note'], options)
end
if not options.simple and item.tags['warning'] then
frontend.render_tag(codedoc, 'warning', item.tags['warning'], options)
end
if not options.simple and item.tags['fixme'] then
frontend.render_tag(codedoc, 'fixme', item.tags['fixme'], options)
end
if not options.simple and item.tags['todo'] then
frontend.render_tag(codedoc, 'todo', item.tags['todo'], options)
hasTodoItems = true
end
if not options.simple and item.tags['usage'] then
frontend.render_tag(codedoc, 'usage', item.tags['usage'], options, frontend.pretask_usage_highlight)
end
if not options.simple and item.tags['see'] then
frontend.render_tag(codedoc, 'see', item.tags['see'], options)
end
end
end
-- Render module-level annotations.
local header_level = options.code and 'h3' or 'h2'
local header_text
for _, tag_name in ipairs(p.tags._annotation_hierarchy) do
if data.tags[tag_name] then
header_text = i18n:msg('tag-' .. tag_name, data.tags[tag_name].value and '1' or '2')
header_text = '<' .. header_level .. '>' .. ' ' .. header_text .. ' ' .. '</' .. header_level .. '>'
codedoc:newline():wikitext(header_text):newline()
if data.tags[tag_name].value then
codedoc:wikitext(data.tags[tag_name].value):newline()
else
for _, tag_el in ipairs(data.tags[tag_name]) do
codedoc:wikitext('* ' .. tag_el.value):newline()
end
end
end
end
-- Byline for autogeneration.
if options.footer then
codedoc:newline()
codedoc:tag('hr'):done()
codedoc:newline()
codedoc:tag('p'):css( 'text-align', 'right' ):wikitext(i18n:msg('message-autogeneration'))
end
-- Add nowiki tags for EOF termination in tests.
codedoc:tag('nowiki', { selfClosing = true })
-- Code documentation formatting.
codedoc = tohtml(tostring(codedoc))
documentation:wikitext(codedoc)
-- Endmatter table for module information.
if options.card then
local endmatter = mw.html.create('table')
endmatter:addClass('wikitable')
endmatter:css('width', '75%')
endmatter:css('margin', '0 auto 1em auto')
local module_type = data.type == 'module'
and i18n:msg('invocable-module')
or i18n:msg('meta-module')
endmatter
:tag('caption')
:wikitext('Module information card (autogenerated)')
endmatter
:tag('tr')
:tag('th'):attr('scope', 'col'):wikitext('Name'):done()
:tag('th'):attr('scope', 'col'):wikitext('Value'):done()
if options.image or data.info and data.info['image'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('endmatter-image')):done()
:tag('td')
:attr('align', 'center')
:wikitext('[[File:' .. (options.image or data.info['image']) .. '|270px|center]]')
:done()
:done()
end
if options.caption or data.info and data.info['caption'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('endmatter-caption')):done()
:tag('td')
:attr('align', options.image or data.info['image'] and 'center' or 'left')
:wikitext(tohtml(options.caption or data.info['caption']))
:done()
:done()
end
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext((gsub(i18n:msg('type-variable'), '^%w', mw.ustring.upper))):done()
:tag('td')
:attr('align', options.image or data.info and data.info['image'] and 'center' or 'left')
:wikitext(mw.text.tag({
name = options.image or data.info and data.info['image'] and 'h1' or 'span',
content = mw.text.tag({
name = 'b',
content = mw.text.tag({
name = 'code',
content = data.name or DEFAULT_VARIABLE
})
})
})):done()
:done()
:tag('tr')
:tag('td'):wikitext(i18n:msg('code')):done()
:tag('td'):wikitext(tohtml('[[' .. data.filename .. ']]')):done()
:done()
if data.info and data.info['release'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('release-status')):done()
:tag('td'):wikitext(tohtml(data.info['release'])):done()
:done()
end
if data.summary then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('description')):done()
:tag('td'):wikitext(tohtml(data.summary)):done()
:done()
end
if data.info and data.info['author'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('author')):done()
:tag('td'):newline():wikitext(tohtml(data.info['author'])):done()
:done()
end
if data.info and data.info['attribution'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('using-code-by')):done()
:tag('td'):wikitext(tohtml(data.info['attribution'])):done()
:done()
end
if data.info and data.info['credit'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('other-attribution')):done()
:tag('td'):wikitext(tohtml(data.info['credit'])):done()
:done()
end
if data.info and data.info['require'] then
data.info['require'] = data.info['require']
:gsub('^[^[%s]+$', '[[%1]]')
:gsub('%* ([^[%s]+)', '* [[%1]]')
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('dependencies')):done()
:tag('td'):newline():wikitext(tohtml(data.info['require'])):done()
:done()
end
if data.info and data.info['demo'] then
endmatter
:tag('tr')
:tag('td'):attr('scope', 'row'):wikitext(i18n:msg('examples')):done()
:tag('td'):wikitext(tohtml(data.info['demo'])):done()
:done()
end
endmatter:newline()
endmatter = tostring(endmatter)
documentation:newline():newline()
documentation:wikitext(endmatter)
end
documentation = tostring(documentation)
if hasTodoItems and options.autodoc
and mw.title.new('Module:' .. options.file).fullText == mw.title.getCurrentTitle().fullText then
documentation = documentation .. '[[Category:' .. i18n:msg('todo') .. ']]'
end
if mw.title.new('Module:Docbunto/styles.css').exists then
documentation = documentation .. frame:extensionTag('templatestyles', '', {src = 'Module:Docbunto/styles.css'})
end
return documentation
end
--- Token dictionary for Docbunto tags.
-- Maps Docbunto tag names to tag tokens.
-- * Multi-line tags use the `'M'` token.
-- * Multi-line preformatted tags use the `'ML'` token.
-- * Identifier tags use the `'ID'` token.
-- * Single-line tags use the `'S'` token.
-- * Flags use the `'N'` token.
-- * Type tags use the `'T'` token.
-- @table p.tags
p.tags = {
-- Item-level tags, available for global use.
['param'] = 'M', ['see'] = 'M', ['note'] = 'M', ['usage'] = 'ML',
['description'] = 'M', ['field'] = 'M', ['return'] = 'M',
['fixme'] = 'M', ['todo'] = 'M', ['warning'] = 'M', ['error'] = 'M';
['class'] = 'ID', ['name'] = 'ID', ['alias'] = 'ID';
['summary'] = 'S', ['pragma'] = 'S', ['factory'] = 'S',
['release'] = 'S', ['author'] = 'S', ['copyright'] = 'S', ['license'] = 'S',
['image'] = 'S', ['caption'] = 'S', ['require'] = 'S', ['attribution'] = 'S',
['credit'] = 'S', ['demo'] = 'S';
['local'] = 'N', ['export'] = 'N', ['private'] = 'N', ['constructor'] = 'N',
['static'] = 'N';
-- Project-level tags, all scoped to a file.
['module'] = 'T', ['script'] = 'T', ['classmod'] = 'T', ['topic'] = 'T',
['submodule'] = 'T', ['example'] = 'T', ['file'] = 'T';
-- Module-level tags, used to register module items.
['function'] = 'T', ['table'] = 'T', ['member'] = 'T', ['variable'] = 'T',
['section'] = 'T', ['type'] = 'T';
}
p.tags._alias = {
-- Normal aliases.
['about'] = 'summary',
['abstract'] = 'summary',
['brief'] = 'summary',
['bug'] = 'fixme',
['argument'] = 'param',
['credits'] = 'credit',
['code'] = 'usage',
['details'] = 'description',
['discussion'] = 'description',
['exception'] = 'error',
['lfunction'] = 'function',
['package'] = 'module',
['property'] = 'member',
['raise'] = 'error',
['requires'] = 'require',
['returns'] = 'return',
['throws'] = 'error',
['typedef'] = 'type',
-- Typed aliases.
['bool'] = 'field',
['func'] = 'field',
['int'] = 'field',
['number'] = 'field',
['string'] = 'field',
['tab'] = 'field',
['vararg'] = 'param',
['tfield'] = 'field',
['tparam'] = 'param',
['treturn'] = 'return'
}
p.tags._type_alias = {
-- Implicit type value alias.
['bool'] = 'boolean',
['func'] = 'function',
['int'] = 'number',
['number'] = 'number',
['string'] = 'string',
['tab'] = 'table',
['vararg'] = '...',
-- Pure typed modifier alias.
['tfield'] = 'variable',
['tparam'] = 'variable',
['treturn'] = 'variable'
}
p.tags._project_level = {
-- Contains code.
['module'] = true,
['script'] = true,
['classmod'] = true,
['submodule'] = true,
['file'] = true,
-- Contains documentation.
['topic'] = true,
['example'] = true
}
p.tags._code_types = {
['module'] = true,
['script'] = true,
['classmod'] = true
}
p.tags._module_info = {
['image'] = true,
['caption'] = true,
['release'] = true,
['author'] = true,
['copyright'] = true,
['license'] = true,
['require'] = true,
['credit'] = true,
['attribution'] = true,
['demo'] = true
}
p.tags._annotation_tags = {
['field'] = true,
['warning'] = true,
['fixme'] = true,
['note'] = true,
['todo'] = true,
['see'] = true
}
p.tags._annotation_hierarchy = {
'warning',
'fixme',
'note',
'todo',
'see'
}
p.tags._privacy_tags = {
['private'] = true,
['local'] = true
}
p.tags._generic_tags = {
['variable'] = true,
['member'] = true
}
p.tags._subtype_tags = {
['factory'] = true,
['local'] = true,
['private'] = true,
['constructor'] = true,
['static'] = true
}
p.tags._subtype_hierarchy = {
'private',
'local',
'static',
'factory',
'constructor'
}
return p
97e0wti9mg3650r9kw1xqydjisw6dtc
Module:Docbunto/references
828
101352
803404
2024-09-25T06:37:59Z
en>Awesome Aasim
0
copy from testwiki, based on work from [[wikia:w:c:dev:Module:Docbunto/references]]
803404
Scribunto
text/plain
--- Reference table for Docbunto reference links.
-- List of global functions and tables available in Scribunto.
-- @file docbunto_references
--- Reference data map for types.
-- @property {table} docbunto_references.types
--- Reference link map for global items.
-- @property {table} docbunto_references.items
return {
types = {
-- Lua types available to Scribunto.
["nil"] = { link = "mw:Extension:Scribunto/Lua reference manual#nil", name = "nil" },
["bool"] = { link = "mw:Extension:Scribunto/Lua reference manual#boolean", name = "boolean" },
["boolean"] = { link = "mw:Extension:Scribunto/Lua reference manual#boolean", name = "boolean" },
["string"] = { link = "mw:Extension:Scribunto/Lua reference manual#string", name = "string" },
["int"] = { link = "mw:Extension:Scribunto/Lua reference manual#number", name = "number" },
["number"] = { link = "mw:Extension:Scribunto/Lua reference manual#number", name = "number" },
["tab"] = { link = "mw:Extension:Scribunto/Lua reference manual#table", name = "table" },
["table"] = { link = "mw:Extension:Scribunto/Lua reference manual#table", name = "table" },
["sequence"] = { link = "mw:Extension:Scribunto/Lua reference manual#sequence", name = "sequence" },
["func"] = { link = "mw:Extension:Scribunto/Lua reference manual#function", name = "function" },
["function"] = { link = "mw:Extension:Scribunto/Lua reference manual#function", name = "function" },
-- Scribunto library objects.
["mw.loadData"] = { link = "mw:Extension:Scribunto/Lua reference manual#mw.loadData", name = "DataWrapper" },
["DataWrapper"] = { link = "mw:Extension:Scribunto/Lua reference manual#mw.loadData", name = "DataWrapper" },
["Frame"] = { link = "mw:Extension:Scribunto/Lua reference manual#Frame object", name = "Frame" },
["mw.language"] = { link = "mw:Extension:Scribunto/Lua reference manual#Language object", name = "Lang" },
["Lang"] = { link = "mw:Extension:Scribunto/Lua reference manual#Language object", name = "Lang" },
["mw.uri"] = { link = "mw:Extension:Scribunto/Lua reference manual#URI object", name = "Uri" },
["Uri"] = { link = "mw:Extension:Scribunto/Lua reference manual#URI object", name = "Uri" },
["mw.html"] = { link = "mw:Extension:Scribunto/Lua reference manual#HTML object", name = "HTMLBuilder" },
["HTMLBuilder"] = { link = "mw:Extension:Scribunto/Lua reference manual#HTML object", name = "HTMLBuilder" },
["mw.title"] = { link = "mw:Extension:Scribunto/Lua reference manual#Title objects", name = "Title" },
["Title"] = { link = "mw:Extension:Scribunto/Lua reference manual#Title objects", name = "Title" },
["mw.message"] = { link = "mw:Extension:Scribunto/Lua reference manual#Message object", name = "Msg" },
["Msg"] = { link = "mw:Extension:Scribunto/Lua reference manual#Message object", name = "Msg" },
},
items = {
-- Types and primitives.
["..."] = "mw:Extension:Scribunto/Lua reference manual#varargs",
["nil"] = "mw:Extension:Scribunto/Lua reference manual#nil",
["boolean"] = "mw:Extension:Scribunto/Lua reference manual#boolean",
["number"] = "mw:Extension:Scribunto/Lua reference manual#number",
["sequence"] = "mw:Extension:Scribunto/Lua reference manual#sequence",
["function"] = "mw:Extension:Scribunto/Lua reference manual#function",
-- Global variables and functions.
["_G"] = "mw:Extension:Scribunto/Lua reference manual#_G",
["_VERSION"] = "mw:Extension:Scribunto/Lua reference manual#_VERSION",
["assert"] = "mw:Extension:Scribunto/Lua reference manual#assert",
["error"] = "mw:Extension:Scribunto/Lua reference manual#error",
["getmetatable"] = "mw:Extension:Scribunto/Lua reference manual#getmetatable",
["setmetatable"] = "mw:Extension:Scribunto/Lua reference manual#setmetatable",
["ipairs"] = "mw:Extension:Scribunto/Lua reference manual#ipairs",
["next"] = "mw:Extension:Scribunto/Lua reference manual#next",
["pairs"] = "mw:Extension:Scribunto/Lua reference manual#pairs",
["pcall"] = "mw:Extension:Scribunto/Lua reference manual#pcall",
["xpcall"] = "mw:Extension:Scribunto/Lua reference manual#xpcall",
["rawequal"] = "mw:Extension:Scribunto/Lua reference manual#rawequal",
["rawget"] = "mw:Extension:Scribunto/Lua reference manual#rawget",
["rawset"] = "mw:Extension:Scribunto/Lua reference manual#rawset",
["require"] = "mw:Extension:Scribunto/Lua reference manual#require",
["select"] = "mw:Extension:Scribunto/Lua reference manual#select",
["tonumber"] = "mw:Extension:Scribunto/Lua reference manual#tonumber",
["tostring"] = "mw:Extension:Scribunto/Lua reference manual#tostring",
["type"] = "mw:Extension:Scribunto/Lua reference manual#type",
["unpack"] = "mw:Extension:Scribunto/Lua reference manual#unpack",
-- Lua debug library.
["debug"] = "mw:Extension:Scribunto/Lua reference manual#Debug library",
["debug.traceback"] = "mw:Extension:Scribunto/Lua reference manual#debug.traceback",
-- Lua math library.
["math"] = "mw:Extension:Scribunto/Lua reference manual#Math library",
["math.abs"] = "mw:Extension:Scribunto/Lua reference manual#math.abs",
["math.acos"] = "mw:Extension:Scribunto/Lua reference manual#math.acos",
["math.asin"] = "mw:Extension:Scribunto/Lua reference manual#math.asin",
["math.atan"] = "mw:Extension:Scribunto/Lua reference manual#math.atan",
["math.atan2"] = "mw:Extension:Scribunto/Lua reference manual#math.atan2",
["math.ceil"] = "mw:Extension:Scribunto/Lua reference manual#math.ceil",
["math.cos"] = "mw:Extension:Scribunto/Lua reference manual#math.cos",
["math.cosh"] = "mw:Extension:Scribunto/Lua reference manual#math.cosh",
["math.deg"] = "mw:Extension:Scribunto/Lua reference manual#math.deg",
["math.exp"] = "mw:Extension:Scribunto/Lua reference manual#math.exp",
["math.floor"] = "mw:Extension:Scribunto/Lua reference manual#math.floor",
["math.fmod"] = "mw:Extension:Scribunto/Lua reference manual#math.fmod",
["math.frexp"] = "mw:Extension:Scribunto/Lua reference manual#math.frexp",
["math.huge"] = "mw:Extension:Scribunto/Lua reference manual#math.huge",
["math.ldexp"] = "mw:Extension:Scribunto/Lua reference manual#math.ldexp",
["math.log"] = "mw:Extension:Scribunto/Lua reference manual#math.log",
["math.log10"] = "mw:Extension:Scribunto/Lua reference manual#math.log10",
["math.max"] = "mw:Extension:Scribunto/Lua reference manual#math.max",
["math.min"] = "mw:Extension:Scribunto/Lua reference manual#math.min",
["math.modf"] = "mw:Extension:Scribunto/Lua reference manual#math.modf",
["math.pi"] = "mw:Extension:Scribunto/Lua reference manual#math.pi",
["math.pow"] = "mw:Extension:Scribunto/Lua reference manual#math.pow",
["math.rad"] = "mw:Extension:Scribunto/Lua reference manual#math.rad",
["math.random"] = "mw:Extension:Scribunto/Lua reference manual#math.random",
["math.randomseed"] = "mw:Extension:Scribunto/Lua reference manual#math.randomseed",
["math.sin"] = "mw:Extension:Scribunto/Lua reference manual#math.sin",
["math.sinh"] = "mw:Extension:Scribunto/Lua reference manual#math.sinh",
["math.sqrt"] = "mw:Extension:Scribunto/Lua reference manual#math.sqrt",
["math.tan"] = "mw:Extension:Scribunto/Lua reference manual#math.tan",
["math.tanh"] = "mw:Extension:Scribunto/Lua reference manual#math.tanh",
-- Operating system library.
["os"] = "mw:Extension:Scribunto/Lua reference manual#Operating system library",
["os.clock"] = "mw:Extension:Scribunto/Lua reference manual#os.clock",
["os.date"] = "mw:Extension:Scribunto/Lua reference manual#os.date",
["os.difftime"] = "mw:Extension:Scribunto/Lua reference manual#os.difftime",
["os.time"] = "mw:Extension:Scribunto/Lua reference manual#os.time",
-- Package library.
["package"] = "mw:Extension:Scribunto/Lua reference manual#Package library",
["package.loaded"] = "mw:Extension:Scribunto/Lua reference manual#package.loaded",
["package.loaders"] = "mw:Extension:Scribunto/Lua reference manual#package.loaders",
["package.preload"] = "mw:Extension:Scribunto/Lua reference manual#package.preload",
["package.seeall"] = "mw:Extension:Scribunto/Lua reference manual#package.seeall",
-- Lua string library.
["string"] = "mw:Extension:Scribunto/Lua reference manual#String library",
["string.byte"] = "mw:Extension:Scribunto/Lua reference manual#string.byte",
["string.char"] = "mw:Extension:Scribunto/Lua reference manual#string.char",
["string.find"] = "mw:Extension:Scribunto/Lua reference manual#string.find",
["string.format"] = "mw:Extension:Scribunto/Lua reference manual#string.format",
["string.gmatch"] = "mw:Extension:Scribunto/Lua reference manual#string.gmatch",
["string.gsub"] = "mw:Extension:Scribunto/Lua reference manual#string.gsub",
["string.len"] = "mw:Extension:Scribunto/Lua reference manual#string.len",
["string.lower"] = "mw:Extension:Scribunto/Lua reference manual#string.lower",
["string.match"] = "mw:Extension:Scribunto/Lua reference manual#string.match",
["string.rep"] = "mw:Extension:Scribunto/Lua reference manual#string.rep",
["string.reverse"] = "mw:Extension:Scribunto/Lua reference manual#string.reverse",
["string.sub"] = "mw:Extension:Scribunto/Lua reference manual#string.sub",
["string.upper"] = "mw:Extension:Scribunto/Lua reference manual#string.upper",
-- Lua table library.
["table"] = "mw:Extension:Scribunto/Lua reference manual#Table library",
["table.concat"] = "mw:Extension:Scribunto/Lua reference manual#table.concat",
["table.insert"] = "mw:Extension:Scribunto/Lua reference manual#table.insert",
["table.maxn"] = "mw:Extension:Scribunto/Lua reference manual#table.maxn",
["table.remove"] = "mw:Extension:Scribunto/Lua reference manual#table.remove",
["table.sort"] = "mw:Extension:Scribunto/Lua reference manual#table.sort",
-- MediaWiki Scribunto library.
["mw"] = "mw:Extension:Scribunto/Lua reference manual",
["mw.allToString"] = "mw:Extension:Scribunto/Lua reference manual#mw.allToString",
["mw.clearLogBuffer"] = "mw:Extension:Scribunto/Lua reference manual#mw.clearLogBuffer",
["mw.clone"] = "mw:Extension:Scribunto/Lua reference manual#mw.clone",
["mw.executeFunction"] = "mw:Extension:Scribunto/Lua reference manual#mw.executeFunction",
["mw.executeModule"] = "mw:Extension:Scribunto/Lua reference manual#mw.executeModule",
["mw.getCurrentFrame"] = "mw:Extension:Scribunto/Lua reference manual#mw.getCurrentFrame",
["mw.getLogBuffer"] = "mw:Extension:Scribunto/Lua reference manual#mw.getLogBuffer",
["mw.incrementExpensiveFunctionCount"] = "mw:Extension:Scribunto/Lua reference manual#mw.incrementExpensiveFunctionCount",
["mw.loadData"] = "mw:Extension:Scribunto/Lua reference manual#mw.loadData",
["DataWrapper"] = "mw:Extension:Scribunto/Lua reference manual#mw.loadData",
["mw.log"] = "mw:Extension:Scribunto/Lua reference manual#mw.log",
["print"] = "mw:Extension:Scribunto/Lua reference manual#mw.log",
-- Scribunto frame object.
["frame"] = "mw:Extension:Scribunto/Lua reference manual#Frame object",
["frame.args"] = "mw:Extension:Scribunto/Lua reference manual#frame.args",
["frame:getParent"] = "mw:Extension:Scribunto/Lua reference manual#frame:getParent",
["frame:expandTemplate"] = "mw:Extension:Scribunto/Lua reference manual#frame:expandTemplate",
["frame:preprocess"] = "mw:Extension:Scribunto/Lua reference manual#frame:preprocess",
["frame:getArgument"] = "mw:Extension:Scribunto/Lua reference manual#frame:getArgument",
["frame:newParserValue"] = "mw:Extension:Scribunto/Lua reference manual#frame:newParserValue",
["frame:newTemplateParserValue"] = "mw:Extension:Scribunto/Lua reference manual#frame:newTemplateParserValue",
["frame:argumentPairs"] = "mw:Extension:Scribunto/Lua reference manual#frame:argumentPairs",
["frame:getTitle"] = "mw:Extension:Scribunto/Lua reference manual#frame:getTitle",
-- Scribunto frame object as `Frame` class.
["Frame"] = "mw:Extension:Scribunto/Lua reference manual#Frame object",
["Frame.args"] = "mw:Extension:Scribunto/Lua reference manual#frame.args",
["Frame:getParent"] = "mw:Extension:Scribunto/Lua reference manual#frame:getParent",
["Frame:expandTemplate"] = "mw:Extension:Scribunto/Lua reference manual#frame:expandTemplate",
["Frame:preprocess"] = "mw:Extension:Scribunto/Lua reference manual#frame:preprocess",
["Frame:getArgument"] = "mw:Extension:Scribunto/Lua reference manual#frame:getArgument",
["Frame:newParserValue"] = "mw:Extension:Scribunto/Lua reference manual#frame:newParserValue",
["Frame:newTemplateParserValue"] = "mw:Extension:Scribunto/Lua reference manual#frame:newTemplateParserValue",
["Frame:argumentPairs"] = "mw:Extension:Scribunto/Lua reference manual#frame:argumentPairs",
["Frame:getTitle"] = "mw:Extension:Scribunto/Lua reference manual#frame:getTitle",
-- Scribunto language library.
["mw.language"] = "mw:Extension:Scribunto/Lua reference manual#Language library",
["mw.language.fetchLanguageName"] = "mw:Extension:Scribunto/Lua reference manual#mw.language.fetchLanguageName",
["mw.language.getContentLanguage"] = "mw:Extension:Scribunto/Lua reference manual#mw.language.getContentLanguage",
["mw.language.isValidBuiltInCode"] = "mw:Extension:Scribunto/Lua reference manual#mw.language.isValidBuiltInCode",
["mw.language.isValidCode"] = "mw:Extension:Scribunto/Lua reference manual#mw.language.isValidCode",
["mw.language.new"] = "mw:Extension:Scribunto/Lua reference manual#mw.language.new",
-- Scribunto language object.
["lang"] = "mw:Extension:Scribunto/Lua reference manual#Language object",
["mw.language:getCode"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:getCode",
["mw.language:isRTL"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:isRTL",
["mw.language:lc"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:lc",
["mw.language:lcfirst"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:lcfirst",
["mw.language:uc"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:uc",
["mw.language:ucfirst"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:ucfirst",
["mw.language:caseFold"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:caseFold",
["mw.language:formatNum"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:formatNum",
["mw.language:formatDate"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:formatDate",
["mw.language:parseFormattedNumber"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:parseFormattedNumber",
["mw.language:convertPlural"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:convertPlural",
["mw.language:convertGrammar"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:convertGrammar",
["mw.language:gender"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:gender",
-- Scribunto language object as `Lang` class.
["Lang"] = "mw:Extension:Scribunto/Lua reference manual#Language object",
["Lang:getCode"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:getCode",
["Lang:isRTL"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:isRTL",
["Lang:lc"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:lc",
["Lang:lcfirst"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:lcfirst",
["Lang:uc"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:uc",
["Lang:ucfirst"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:ucfirst",
["Lang:caseFold"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:caseFold",
["Lang:formatNum"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:formatNum",
["Lang:formatDate"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:formatDate",
["Lang:parseFormattedNumber"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:parseFormattedNumber",
["Lang:convertPlural"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:convertPlural",
["Lang:convertGrammar"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:convertGrammar",
["Lang:gender"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:gender",
-- Scribunto site library.
["mw.site"] = "mw:Extension:Scribunto/Lua reference manual#Site library",
["mw.site.currentVersion"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.currentVersion",
["mw.site.scriptPath"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.scriptPath",
["mw.site.server"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.server",
["mw.site.siteName"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.siteName",
["mw.site.stylePath"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.stylePath",
["mw.site.namespaces"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.namespaces",
["mw.site.contentNamespaces"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.contentNamespaces",
["mw.site.subjectNamespaces"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.subjectNamespaces",
["mw.site.talkNamespaces"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.talkNamespaces",
["mw.site.sassParams"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.sassParams",
["mw.site.stats"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.stats",
["mw.site.stats.pagesInCategory"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.stats.pagesInCategory",
["mw.site.stats.pagesInNamespace"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.stats.pagesInNamespace",
["mw.site.stats.usersInGroup"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.stats.usersInGroup",
-- Scribunto URI library.
["mw.uri"] = "mw:Extension:Scribunto/Lua reference manual#Uri library",
["mw.uri.encode"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.encode",
["mw.uri.decode"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.decode",
["mw.uri.anchorEncode"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.anchorEncode",
["mw.uri.buildQueryString"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.buildQueryString",
["mw.uri.parseQueryString"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.parseQueryString",
["mw.uri.canonicalUrl"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.canonicalUrl",
["mw.uri.fullUrl"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.fullUrl",
["mw.uri.localUrl"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.localUrl",
["mw.uri.new"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.new",
["mw.uri.validate"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.validate",
-- Scribunto URI object.
["uri"] = "mw:Extension:Scribunto/Lua reference manual#URI object",
["mw.uri:parse"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:parse",
["mw.uri:clone"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:clone",
["mw.uri:extend"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:extend",
-- Scribunto URI object as `Uri` class.
["Uri"] = "mw:Extension:Scribunto/Lua reference manual#URI object",
["Uri:parse"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:parse",
["Uri:parse"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:parse",
["Uri:clone"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:clone",
["Uri:extend"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:extend",
-- Scribunto UTF8-safe string library.
["mw.ustring"] = "mw:Extension:Scribunto/Lua reference manual#Ustring library",
["mw.ustring.maxPatternLength"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.maxPatternLength",
["mw.ustring.maxStringLength"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.maxStringLength",
["mw.ustring.byte"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.byte",
["mw.ustring.byteoffset"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.byteoffset",
["mw.ustring.char"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.char",
["mw.ustring.codepoint"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.codepoint",
["mw.ustring.find"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.find",
["mw.ustring.format"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.format",
["mw.ustring.gcodepoint"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.gcodepoint",
["mw.ustring.gmatch"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.gmatch",
["mw.ustring.gsub"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.gsub",
["mw.ustring.isutf8"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.isutf8",
["mw.ustring.len"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.len",
["mw.ustring.lower"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.lower",
["mw.ustring.match"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.match",
["mw.ustring.rep"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.rep",
["mw.ustring.sub"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.sub",
["mw.ustring.toNFC"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.toNFC",
["mw.ustring.toNFD"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.toNFD",
["mw.ustring.upper"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.upper",
-- Scribunto HTML library.
["mw.html"] = "mw:Extension:Scribunto/Lua reference manual#HTML library",
["mw.html.create"] = "mw:Extension:Scribunto/Lua reference manual#mw.html.create",
-- Scribunto HTML object.
["html"] = "mw:Extension:Scribunto/Lua reference manual#HTML object",
["mw.html:node"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:node",
["mw.html:wikitext"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:wikitext",
["mw.html:newline"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:newline",
["mw.html:tag"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:tag",
["mw.html:attr"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:attr",
["mw.html:getAttr"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:getAttr",
["mw.html:addClass"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:addClass",
["mw.html:css"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:css",
["mw.html:cssText"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:cssText",
["mw.html:done"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:done",
["mw.html:allDone"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:allDone",
-- Scribunto HTML object as `HTMLBuilder` class.
["HTMLBuilder"] = "mw:Extension:Scribunto/Lua reference manual#HTML object",
["HTMLBuilder:node"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:node",
["HTMLBuilder:wikitext"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:wikitext",
["HTMLBuilder:newline"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:newline",
["HTMLBuilder:tag"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:tag",
["HTMLBuilder:attr"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:attr",
["HTMLBuilder:getAttr"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:getAttr",
["HTMLBuilder:addClass"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:addClass",
["HTMLBuilder:css"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:css",
["HTMLBuilder:cssText"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:cssText",
["HTMLBuilder:done"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:done",
["HTMLBuilder:allDone"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:allDone",
-- Scribunto text library.
["mw.text"] = "mw:Extension:Scribunto/Lua reference manual#Text library",
["mw.text.decode"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.decode",
["mw.text.encode"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.encode",
["mw.text.listToText"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.listToText",
["mw.text.nowiki"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.nowiki",
["mw.text.split"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.split",
["mw.text.gsplit"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.gsplit",
["mw.text.tag"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.tag",
["mw.text.trim"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.trim",
["mw.text.truncate"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.truncate",
["mw.text.unstrip"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.unstrip",
-- Scribunto title library.
["mw.title"] = "mw:Extension:Scribunto/Lua reference manual#Title library",
["mw.title.equals"] = "mw:Extension:Scribunto/Lua reference manual#mw.title.equals",
["mw.title.compare"] = "mw:Extension:Scribunto/Lua reference manual#mw.title.compare",
["mw.title.getCurrentTitle"] = "mw:Extension:Scribunto/Lua reference manual#mw.title.getCurrentTile",
["mw.title.new"] = "mw:Extension:Scribunto/Lua reference manual#mw.title.new",
["mw.title.makeTitle"] = "mw:Extension:Scribunto/Lua reference manual#mw.title.makeTitle",
-- Scribunto title object.
["mw.title.id"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.interwiki"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.namespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.fragment"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.nsText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.subjectNsText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.text"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.prefixedText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.fullText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.rootText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.baseText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.subpageText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.canTalk"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.exists"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.fileExists"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isContentPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isExternal"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isLocal"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isRedirect"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isSpecialPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isSubpage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isTalkPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:isSubpageOf"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:inNamespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:inNamespaces"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:hasSubjectNamespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.basePageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.rootPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.talkPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.subjectPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.protectionLevels"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:subPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:partialUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:fullUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:localUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:canonicalUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:getContent"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
-- Scribunto title object as `Title` class.
["Title"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.id"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.interwiki"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.namespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.fragment"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.nsText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.subjectNsText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.text"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.prefixedText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.fullText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.rootText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.baseText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.subpageText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.canTalk"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.exists"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.fileExists"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isContentPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isExternal"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isLocal"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isRedirect"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isSpecialPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isSubpage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isTalkPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:isSubpageOf"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:inNamespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:inNamespaces"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:hasSubjectNamespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.basePageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.rootPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.talkPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.subjectPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.protectionLevels"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:subPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:partialUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:fullUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:localUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:canonicalUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:getContent"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
-- Scribunto message library.
["mw.message"] = "mw:Extension:Scribunto/Lua reference manual#Message library",
["mw.message.new"] = "mw:Extension:Scribunto/Lua reference manual#mw.message.new",
["mw.message.newFallbackSequence"] = "mw:Extension:Scribunto/Lua reference manual#mw.message.newFallbackSequence",
["mw.message.rawParam"] = "mw:Extension:Scribunto/Lua reference manual#mw.message.rawParam",
["mw.message.numParam"] = "mw:Extension:Scribunto/Lua reference manual#mw.message.numParam",
["mw.message.getDefaultLanguage"] = "mw:Extension:Scribunto/Lua reference manual#mw.message.getDefaultLanguage",
-- Scribunto message object.
["msg"] = "mw:Extension:Scribunto/Lua reference manual#Message object",
["mw.message:params"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:params",
["mw.message:rawParams"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:rawParams",
["mw.message:numParams"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:numParams",
["mw.message:inLanguage"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:inLanguage",
["mw.message:useDatabase"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:useDatabase",
["mw.message:plain"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:plain",
["mw.message:exists"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:exists",
["mw.message:isBlank"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:isBlank",
["mw.message:isDisabled"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:isDisabled",
-- Scribunto message object as `Msg` class.
["Msg"] = "mw:Extension:Scribunto/Lua reference manual#Message object",
["Msg:params"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:params",
["Msg:rawParams"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:rawParams",
["Msg:numParams"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:numParams",
["Msg:inLanguage"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:inLanguage",
["Msg:useDatabase"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:useDatabase",
["Msg:plain"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:plain",
["Msg:exists"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:exists",
["Msg:isBlank"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:isBlank",
["Msg:isDisabled"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:isDisabled",
}
}
mjxb8skj02djmabwzn0kbg2eptjb0r6
803405
803404
2026-07-29T23:41:28Z
SM7
3953
1 revision imported from [[:en:Module:Docbunto/references]]
803404
Scribunto
text/plain
--- Reference table for Docbunto reference links.
-- List of global functions and tables available in Scribunto.
-- @file docbunto_references
--- Reference data map for types.
-- @property {table} docbunto_references.types
--- Reference link map for global items.
-- @property {table} docbunto_references.items
return {
types = {
-- Lua types available to Scribunto.
["nil"] = { link = "mw:Extension:Scribunto/Lua reference manual#nil", name = "nil" },
["bool"] = { link = "mw:Extension:Scribunto/Lua reference manual#boolean", name = "boolean" },
["boolean"] = { link = "mw:Extension:Scribunto/Lua reference manual#boolean", name = "boolean" },
["string"] = { link = "mw:Extension:Scribunto/Lua reference manual#string", name = "string" },
["int"] = { link = "mw:Extension:Scribunto/Lua reference manual#number", name = "number" },
["number"] = { link = "mw:Extension:Scribunto/Lua reference manual#number", name = "number" },
["tab"] = { link = "mw:Extension:Scribunto/Lua reference manual#table", name = "table" },
["table"] = { link = "mw:Extension:Scribunto/Lua reference manual#table", name = "table" },
["sequence"] = { link = "mw:Extension:Scribunto/Lua reference manual#sequence", name = "sequence" },
["func"] = { link = "mw:Extension:Scribunto/Lua reference manual#function", name = "function" },
["function"] = { link = "mw:Extension:Scribunto/Lua reference manual#function", name = "function" },
-- Scribunto library objects.
["mw.loadData"] = { link = "mw:Extension:Scribunto/Lua reference manual#mw.loadData", name = "DataWrapper" },
["DataWrapper"] = { link = "mw:Extension:Scribunto/Lua reference manual#mw.loadData", name = "DataWrapper" },
["Frame"] = { link = "mw:Extension:Scribunto/Lua reference manual#Frame object", name = "Frame" },
["mw.language"] = { link = "mw:Extension:Scribunto/Lua reference manual#Language object", name = "Lang" },
["Lang"] = { link = "mw:Extension:Scribunto/Lua reference manual#Language object", name = "Lang" },
["mw.uri"] = { link = "mw:Extension:Scribunto/Lua reference manual#URI object", name = "Uri" },
["Uri"] = { link = "mw:Extension:Scribunto/Lua reference manual#URI object", name = "Uri" },
["mw.html"] = { link = "mw:Extension:Scribunto/Lua reference manual#HTML object", name = "HTMLBuilder" },
["HTMLBuilder"] = { link = "mw:Extension:Scribunto/Lua reference manual#HTML object", name = "HTMLBuilder" },
["mw.title"] = { link = "mw:Extension:Scribunto/Lua reference manual#Title objects", name = "Title" },
["Title"] = { link = "mw:Extension:Scribunto/Lua reference manual#Title objects", name = "Title" },
["mw.message"] = { link = "mw:Extension:Scribunto/Lua reference manual#Message object", name = "Msg" },
["Msg"] = { link = "mw:Extension:Scribunto/Lua reference manual#Message object", name = "Msg" },
},
items = {
-- Types and primitives.
["..."] = "mw:Extension:Scribunto/Lua reference manual#varargs",
["nil"] = "mw:Extension:Scribunto/Lua reference manual#nil",
["boolean"] = "mw:Extension:Scribunto/Lua reference manual#boolean",
["number"] = "mw:Extension:Scribunto/Lua reference manual#number",
["sequence"] = "mw:Extension:Scribunto/Lua reference manual#sequence",
["function"] = "mw:Extension:Scribunto/Lua reference manual#function",
-- Global variables and functions.
["_G"] = "mw:Extension:Scribunto/Lua reference manual#_G",
["_VERSION"] = "mw:Extension:Scribunto/Lua reference manual#_VERSION",
["assert"] = "mw:Extension:Scribunto/Lua reference manual#assert",
["error"] = "mw:Extension:Scribunto/Lua reference manual#error",
["getmetatable"] = "mw:Extension:Scribunto/Lua reference manual#getmetatable",
["setmetatable"] = "mw:Extension:Scribunto/Lua reference manual#setmetatable",
["ipairs"] = "mw:Extension:Scribunto/Lua reference manual#ipairs",
["next"] = "mw:Extension:Scribunto/Lua reference manual#next",
["pairs"] = "mw:Extension:Scribunto/Lua reference manual#pairs",
["pcall"] = "mw:Extension:Scribunto/Lua reference manual#pcall",
["xpcall"] = "mw:Extension:Scribunto/Lua reference manual#xpcall",
["rawequal"] = "mw:Extension:Scribunto/Lua reference manual#rawequal",
["rawget"] = "mw:Extension:Scribunto/Lua reference manual#rawget",
["rawset"] = "mw:Extension:Scribunto/Lua reference manual#rawset",
["require"] = "mw:Extension:Scribunto/Lua reference manual#require",
["select"] = "mw:Extension:Scribunto/Lua reference manual#select",
["tonumber"] = "mw:Extension:Scribunto/Lua reference manual#tonumber",
["tostring"] = "mw:Extension:Scribunto/Lua reference manual#tostring",
["type"] = "mw:Extension:Scribunto/Lua reference manual#type",
["unpack"] = "mw:Extension:Scribunto/Lua reference manual#unpack",
-- Lua debug library.
["debug"] = "mw:Extension:Scribunto/Lua reference manual#Debug library",
["debug.traceback"] = "mw:Extension:Scribunto/Lua reference manual#debug.traceback",
-- Lua math library.
["math"] = "mw:Extension:Scribunto/Lua reference manual#Math library",
["math.abs"] = "mw:Extension:Scribunto/Lua reference manual#math.abs",
["math.acos"] = "mw:Extension:Scribunto/Lua reference manual#math.acos",
["math.asin"] = "mw:Extension:Scribunto/Lua reference manual#math.asin",
["math.atan"] = "mw:Extension:Scribunto/Lua reference manual#math.atan",
["math.atan2"] = "mw:Extension:Scribunto/Lua reference manual#math.atan2",
["math.ceil"] = "mw:Extension:Scribunto/Lua reference manual#math.ceil",
["math.cos"] = "mw:Extension:Scribunto/Lua reference manual#math.cos",
["math.cosh"] = "mw:Extension:Scribunto/Lua reference manual#math.cosh",
["math.deg"] = "mw:Extension:Scribunto/Lua reference manual#math.deg",
["math.exp"] = "mw:Extension:Scribunto/Lua reference manual#math.exp",
["math.floor"] = "mw:Extension:Scribunto/Lua reference manual#math.floor",
["math.fmod"] = "mw:Extension:Scribunto/Lua reference manual#math.fmod",
["math.frexp"] = "mw:Extension:Scribunto/Lua reference manual#math.frexp",
["math.huge"] = "mw:Extension:Scribunto/Lua reference manual#math.huge",
["math.ldexp"] = "mw:Extension:Scribunto/Lua reference manual#math.ldexp",
["math.log"] = "mw:Extension:Scribunto/Lua reference manual#math.log",
["math.log10"] = "mw:Extension:Scribunto/Lua reference manual#math.log10",
["math.max"] = "mw:Extension:Scribunto/Lua reference manual#math.max",
["math.min"] = "mw:Extension:Scribunto/Lua reference manual#math.min",
["math.modf"] = "mw:Extension:Scribunto/Lua reference manual#math.modf",
["math.pi"] = "mw:Extension:Scribunto/Lua reference manual#math.pi",
["math.pow"] = "mw:Extension:Scribunto/Lua reference manual#math.pow",
["math.rad"] = "mw:Extension:Scribunto/Lua reference manual#math.rad",
["math.random"] = "mw:Extension:Scribunto/Lua reference manual#math.random",
["math.randomseed"] = "mw:Extension:Scribunto/Lua reference manual#math.randomseed",
["math.sin"] = "mw:Extension:Scribunto/Lua reference manual#math.sin",
["math.sinh"] = "mw:Extension:Scribunto/Lua reference manual#math.sinh",
["math.sqrt"] = "mw:Extension:Scribunto/Lua reference manual#math.sqrt",
["math.tan"] = "mw:Extension:Scribunto/Lua reference manual#math.tan",
["math.tanh"] = "mw:Extension:Scribunto/Lua reference manual#math.tanh",
-- Operating system library.
["os"] = "mw:Extension:Scribunto/Lua reference manual#Operating system library",
["os.clock"] = "mw:Extension:Scribunto/Lua reference manual#os.clock",
["os.date"] = "mw:Extension:Scribunto/Lua reference manual#os.date",
["os.difftime"] = "mw:Extension:Scribunto/Lua reference manual#os.difftime",
["os.time"] = "mw:Extension:Scribunto/Lua reference manual#os.time",
-- Package library.
["package"] = "mw:Extension:Scribunto/Lua reference manual#Package library",
["package.loaded"] = "mw:Extension:Scribunto/Lua reference manual#package.loaded",
["package.loaders"] = "mw:Extension:Scribunto/Lua reference manual#package.loaders",
["package.preload"] = "mw:Extension:Scribunto/Lua reference manual#package.preload",
["package.seeall"] = "mw:Extension:Scribunto/Lua reference manual#package.seeall",
-- Lua string library.
["string"] = "mw:Extension:Scribunto/Lua reference manual#String library",
["string.byte"] = "mw:Extension:Scribunto/Lua reference manual#string.byte",
["string.char"] = "mw:Extension:Scribunto/Lua reference manual#string.char",
["string.find"] = "mw:Extension:Scribunto/Lua reference manual#string.find",
["string.format"] = "mw:Extension:Scribunto/Lua reference manual#string.format",
["string.gmatch"] = "mw:Extension:Scribunto/Lua reference manual#string.gmatch",
["string.gsub"] = "mw:Extension:Scribunto/Lua reference manual#string.gsub",
["string.len"] = "mw:Extension:Scribunto/Lua reference manual#string.len",
["string.lower"] = "mw:Extension:Scribunto/Lua reference manual#string.lower",
["string.match"] = "mw:Extension:Scribunto/Lua reference manual#string.match",
["string.rep"] = "mw:Extension:Scribunto/Lua reference manual#string.rep",
["string.reverse"] = "mw:Extension:Scribunto/Lua reference manual#string.reverse",
["string.sub"] = "mw:Extension:Scribunto/Lua reference manual#string.sub",
["string.upper"] = "mw:Extension:Scribunto/Lua reference manual#string.upper",
-- Lua table library.
["table"] = "mw:Extension:Scribunto/Lua reference manual#Table library",
["table.concat"] = "mw:Extension:Scribunto/Lua reference manual#table.concat",
["table.insert"] = "mw:Extension:Scribunto/Lua reference manual#table.insert",
["table.maxn"] = "mw:Extension:Scribunto/Lua reference manual#table.maxn",
["table.remove"] = "mw:Extension:Scribunto/Lua reference manual#table.remove",
["table.sort"] = "mw:Extension:Scribunto/Lua reference manual#table.sort",
-- MediaWiki Scribunto library.
["mw"] = "mw:Extension:Scribunto/Lua reference manual",
["mw.allToString"] = "mw:Extension:Scribunto/Lua reference manual#mw.allToString",
["mw.clearLogBuffer"] = "mw:Extension:Scribunto/Lua reference manual#mw.clearLogBuffer",
["mw.clone"] = "mw:Extension:Scribunto/Lua reference manual#mw.clone",
["mw.executeFunction"] = "mw:Extension:Scribunto/Lua reference manual#mw.executeFunction",
["mw.executeModule"] = "mw:Extension:Scribunto/Lua reference manual#mw.executeModule",
["mw.getCurrentFrame"] = "mw:Extension:Scribunto/Lua reference manual#mw.getCurrentFrame",
["mw.getLogBuffer"] = "mw:Extension:Scribunto/Lua reference manual#mw.getLogBuffer",
["mw.incrementExpensiveFunctionCount"] = "mw:Extension:Scribunto/Lua reference manual#mw.incrementExpensiveFunctionCount",
["mw.loadData"] = "mw:Extension:Scribunto/Lua reference manual#mw.loadData",
["DataWrapper"] = "mw:Extension:Scribunto/Lua reference manual#mw.loadData",
["mw.log"] = "mw:Extension:Scribunto/Lua reference manual#mw.log",
["print"] = "mw:Extension:Scribunto/Lua reference manual#mw.log",
-- Scribunto frame object.
["frame"] = "mw:Extension:Scribunto/Lua reference manual#Frame object",
["frame.args"] = "mw:Extension:Scribunto/Lua reference manual#frame.args",
["frame:getParent"] = "mw:Extension:Scribunto/Lua reference manual#frame:getParent",
["frame:expandTemplate"] = "mw:Extension:Scribunto/Lua reference manual#frame:expandTemplate",
["frame:preprocess"] = "mw:Extension:Scribunto/Lua reference manual#frame:preprocess",
["frame:getArgument"] = "mw:Extension:Scribunto/Lua reference manual#frame:getArgument",
["frame:newParserValue"] = "mw:Extension:Scribunto/Lua reference manual#frame:newParserValue",
["frame:newTemplateParserValue"] = "mw:Extension:Scribunto/Lua reference manual#frame:newTemplateParserValue",
["frame:argumentPairs"] = "mw:Extension:Scribunto/Lua reference manual#frame:argumentPairs",
["frame:getTitle"] = "mw:Extension:Scribunto/Lua reference manual#frame:getTitle",
-- Scribunto frame object as `Frame` class.
["Frame"] = "mw:Extension:Scribunto/Lua reference manual#Frame object",
["Frame.args"] = "mw:Extension:Scribunto/Lua reference manual#frame.args",
["Frame:getParent"] = "mw:Extension:Scribunto/Lua reference manual#frame:getParent",
["Frame:expandTemplate"] = "mw:Extension:Scribunto/Lua reference manual#frame:expandTemplate",
["Frame:preprocess"] = "mw:Extension:Scribunto/Lua reference manual#frame:preprocess",
["Frame:getArgument"] = "mw:Extension:Scribunto/Lua reference manual#frame:getArgument",
["Frame:newParserValue"] = "mw:Extension:Scribunto/Lua reference manual#frame:newParserValue",
["Frame:newTemplateParserValue"] = "mw:Extension:Scribunto/Lua reference manual#frame:newTemplateParserValue",
["Frame:argumentPairs"] = "mw:Extension:Scribunto/Lua reference manual#frame:argumentPairs",
["Frame:getTitle"] = "mw:Extension:Scribunto/Lua reference manual#frame:getTitle",
-- Scribunto language library.
["mw.language"] = "mw:Extension:Scribunto/Lua reference manual#Language library",
["mw.language.fetchLanguageName"] = "mw:Extension:Scribunto/Lua reference manual#mw.language.fetchLanguageName",
["mw.language.getContentLanguage"] = "mw:Extension:Scribunto/Lua reference manual#mw.language.getContentLanguage",
["mw.language.isValidBuiltInCode"] = "mw:Extension:Scribunto/Lua reference manual#mw.language.isValidBuiltInCode",
["mw.language.isValidCode"] = "mw:Extension:Scribunto/Lua reference manual#mw.language.isValidCode",
["mw.language.new"] = "mw:Extension:Scribunto/Lua reference manual#mw.language.new",
-- Scribunto language object.
["lang"] = "mw:Extension:Scribunto/Lua reference manual#Language object",
["mw.language:getCode"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:getCode",
["mw.language:isRTL"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:isRTL",
["mw.language:lc"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:lc",
["mw.language:lcfirst"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:lcfirst",
["mw.language:uc"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:uc",
["mw.language:ucfirst"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:ucfirst",
["mw.language:caseFold"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:caseFold",
["mw.language:formatNum"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:formatNum",
["mw.language:formatDate"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:formatDate",
["mw.language:parseFormattedNumber"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:parseFormattedNumber",
["mw.language:convertPlural"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:convertPlural",
["mw.language:convertGrammar"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:convertGrammar",
["mw.language:gender"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:gender",
-- Scribunto language object as `Lang` class.
["Lang"] = "mw:Extension:Scribunto/Lua reference manual#Language object",
["Lang:getCode"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:getCode",
["Lang:isRTL"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:isRTL",
["Lang:lc"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:lc",
["Lang:lcfirst"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:lcfirst",
["Lang:uc"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:uc",
["Lang:ucfirst"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:ucfirst",
["Lang:caseFold"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:caseFold",
["Lang:formatNum"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:formatNum",
["Lang:formatDate"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:formatDate",
["Lang:parseFormattedNumber"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:parseFormattedNumber",
["Lang:convertPlural"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:convertPlural",
["Lang:convertGrammar"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:convertGrammar",
["Lang:gender"] = "mw:Extension:Scribunto/Lua reference manual#mw.language:gender",
-- Scribunto site library.
["mw.site"] = "mw:Extension:Scribunto/Lua reference manual#Site library",
["mw.site.currentVersion"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.currentVersion",
["mw.site.scriptPath"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.scriptPath",
["mw.site.server"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.server",
["mw.site.siteName"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.siteName",
["mw.site.stylePath"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.stylePath",
["mw.site.namespaces"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.namespaces",
["mw.site.contentNamespaces"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.contentNamespaces",
["mw.site.subjectNamespaces"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.subjectNamespaces",
["mw.site.talkNamespaces"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.talkNamespaces",
["mw.site.sassParams"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.sassParams",
["mw.site.stats"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.stats",
["mw.site.stats.pagesInCategory"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.stats.pagesInCategory",
["mw.site.stats.pagesInNamespace"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.stats.pagesInNamespace",
["mw.site.stats.usersInGroup"] = "mw:Extension:Scribunto/Lua reference manual#mw.site.stats.usersInGroup",
-- Scribunto URI library.
["mw.uri"] = "mw:Extension:Scribunto/Lua reference manual#Uri library",
["mw.uri.encode"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.encode",
["mw.uri.decode"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.decode",
["mw.uri.anchorEncode"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.anchorEncode",
["mw.uri.buildQueryString"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.buildQueryString",
["mw.uri.parseQueryString"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.parseQueryString",
["mw.uri.canonicalUrl"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.canonicalUrl",
["mw.uri.fullUrl"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.fullUrl",
["mw.uri.localUrl"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.localUrl",
["mw.uri.new"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.new",
["mw.uri.validate"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri.validate",
-- Scribunto URI object.
["uri"] = "mw:Extension:Scribunto/Lua reference manual#URI object",
["mw.uri:parse"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:parse",
["mw.uri:clone"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:clone",
["mw.uri:extend"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:extend",
-- Scribunto URI object as `Uri` class.
["Uri"] = "mw:Extension:Scribunto/Lua reference manual#URI object",
["Uri:parse"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:parse",
["Uri:parse"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:parse",
["Uri:clone"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:clone",
["Uri:extend"] = "mw:Extension:Scribunto/Lua reference manual#mw.uri:extend",
-- Scribunto UTF8-safe string library.
["mw.ustring"] = "mw:Extension:Scribunto/Lua reference manual#Ustring library",
["mw.ustring.maxPatternLength"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.maxPatternLength",
["mw.ustring.maxStringLength"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.maxStringLength",
["mw.ustring.byte"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.byte",
["mw.ustring.byteoffset"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.byteoffset",
["mw.ustring.char"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.char",
["mw.ustring.codepoint"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.codepoint",
["mw.ustring.find"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.find",
["mw.ustring.format"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.format",
["mw.ustring.gcodepoint"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.gcodepoint",
["mw.ustring.gmatch"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.gmatch",
["mw.ustring.gsub"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.gsub",
["mw.ustring.isutf8"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.isutf8",
["mw.ustring.len"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.len",
["mw.ustring.lower"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.lower",
["mw.ustring.match"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.match",
["mw.ustring.rep"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.rep",
["mw.ustring.sub"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.sub",
["mw.ustring.toNFC"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.toNFC",
["mw.ustring.toNFD"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.toNFD",
["mw.ustring.upper"] = "mw:Extension:Scribunto/Lua reference manual#mw.ustring.upper",
-- Scribunto HTML library.
["mw.html"] = "mw:Extension:Scribunto/Lua reference manual#HTML library",
["mw.html.create"] = "mw:Extension:Scribunto/Lua reference manual#mw.html.create",
-- Scribunto HTML object.
["html"] = "mw:Extension:Scribunto/Lua reference manual#HTML object",
["mw.html:node"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:node",
["mw.html:wikitext"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:wikitext",
["mw.html:newline"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:newline",
["mw.html:tag"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:tag",
["mw.html:attr"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:attr",
["mw.html:getAttr"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:getAttr",
["mw.html:addClass"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:addClass",
["mw.html:css"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:css",
["mw.html:cssText"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:cssText",
["mw.html:done"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:done",
["mw.html:allDone"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:allDone",
-- Scribunto HTML object as `HTMLBuilder` class.
["HTMLBuilder"] = "mw:Extension:Scribunto/Lua reference manual#HTML object",
["HTMLBuilder:node"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:node",
["HTMLBuilder:wikitext"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:wikitext",
["HTMLBuilder:newline"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:newline",
["HTMLBuilder:tag"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:tag",
["HTMLBuilder:attr"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:attr",
["HTMLBuilder:getAttr"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:getAttr",
["HTMLBuilder:addClass"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:addClass",
["HTMLBuilder:css"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:css",
["HTMLBuilder:cssText"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:cssText",
["HTMLBuilder:done"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:done",
["HTMLBuilder:allDone"] = "mw:Extension:Scribunto/Lua reference manual#mw.html:allDone",
-- Scribunto text library.
["mw.text"] = "mw:Extension:Scribunto/Lua reference manual#Text library",
["mw.text.decode"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.decode",
["mw.text.encode"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.encode",
["mw.text.listToText"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.listToText",
["mw.text.nowiki"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.nowiki",
["mw.text.split"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.split",
["mw.text.gsplit"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.gsplit",
["mw.text.tag"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.tag",
["mw.text.trim"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.trim",
["mw.text.truncate"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.truncate",
["mw.text.unstrip"] = "mw:Extension:Scribunto/Lua reference manual#mw.text.unstrip",
-- Scribunto title library.
["mw.title"] = "mw:Extension:Scribunto/Lua reference manual#Title library",
["mw.title.equals"] = "mw:Extension:Scribunto/Lua reference manual#mw.title.equals",
["mw.title.compare"] = "mw:Extension:Scribunto/Lua reference manual#mw.title.compare",
["mw.title.getCurrentTitle"] = "mw:Extension:Scribunto/Lua reference manual#mw.title.getCurrentTile",
["mw.title.new"] = "mw:Extension:Scribunto/Lua reference manual#mw.title.new",
["mw.title.makeTitle"] = "mw:Extension:Scribunto/Lua reference manual#mw.title.makeTitle",
-- Scribunto title object.
["mw.title.id"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.interwiki"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.namespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.fragment"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.nsText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.subjectNsText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.text"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.prefixedText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.fullText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.rootText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.baseText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.subpageText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.canTalk"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.exists"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.fileExists"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isContentPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isExternal"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isLocal"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isRedirect"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isSpecialPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isSubpage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.isTalkPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:isSubpageOf"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:inNamespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:inNamespaces"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:hasSubjectNamespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.basePageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.rootPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.talkPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.subjectPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title.protectionLevels"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:subPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:partialUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:fullUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:localUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:canonicalUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["mw.title:getContent"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
-- Scribunto title object as `Title` class.
["Title"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.id"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.interwiki"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.namespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.fragment"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.nsText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.subjectNsText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.text"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.prefixedText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.fullText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.rootText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.baseText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.subpageText"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.canTalk"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.exists"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.fileExists"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isContentPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isExternal"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isLocal"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isRedirect"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isSpecialPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isSubpage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.isTalkPage"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:isSubpageOf"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:inNamespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:inNamespaces"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:hasSubjectNamespace"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.basePageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.rootPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.talkPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.subjectPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title.protectionLevels"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:subPageTitle"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:partialUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:fullUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:localUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:canonicalUrl"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
["Title:getContent"] = "mw:Extension:Scribunto/Lua reference manual#Title objects",
-- Scribunto message library.
["mw.message"] = "mw:Extension:Scribunto/Lua reference manual#Message library",
["mw.message.new"] = "mw:Extension:Scribunto/Lua reference manual#mw.message.new",
["mw.message.newFallbackSequence"] = "mw:Extension:Scribunto/Lua reference manual#mw.message.newFallbackSequence",
["mw.message.rawParam"] = "mw:Extension:Scribunto/Lua reference manual#mw.message.rawParam",
["mw.message.numParam"] = "mw:Extension:Scribunto/Lua reference manual#mw.message.numParam",
["mw.message.getDefaultLanguage"] = "mw:Extension:Scribunto/Lua reference manual#mw.message.getDefaultLanguage",
-- Scribunto message object.
["msg"] = "mw:Extension:Scribunto/Lua reference manual#Message object",
["mw.message:params"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:params",
["mw.message:rawParams"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:rawParams",
["mw.message:numParams"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:numParams",
["mw.message:inLanguage"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:inLanguage",
["mw.message:useDatabase"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:useDatabase",
["mw.message:plain"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:plain",
["mw.message:exists"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:exists",
["mw.message:isBlank"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:isBlank",
["mw.message:isDisabled"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:isDisabled",
-- Scribunto message object as `Msg` class.
["Msg"] = "mw:Extension:Scribunto/Lua reference manual#Message object",
["Msg:params"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:params",
["Msg:rawParams"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:rawParams",
["Msg:numParams"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:numParams",
["Msg:inLanguage"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:inLanguage",
["Msg:useDatabase"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:useDatabase",
["Msg:plain"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:plain",
["Msg:exists"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:exists",
["Msg:isBlank"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:isBlank",
["Msg:isDisabled"] = "mw:Extension:Scribunto/Lua reference manual#mw.message:isDisabled",
}
}
mjxb8skj02djmabwzn0kbg2eptjb0r6
Module:Lua lexer
828
101353
803406
2026-06-08T03:21:07Z
en>Awesome Aasim
0
remove header but link to MIT license
803406
Scribunto
text/plain
--- Lexer for Lua source code written in pure Lua.
-- @script lexer
-- @license [https://github.com/LoganDark/lua-lexer/blob/master/LICENSE MIT]
-- @author https://github.com/LoganDark
-- @param {string} text Lua source code to lex.
-- @return {string} Table of line arrays containing lexemes.
--- Mapper for individual token list string.
-- @param {string} src List of characters or keywords to map.
-- @param[opt] {table} list Table to extend by reference.
-- @return {{char=true,...}}, map
-- @local
local function lookupify(src, list)
list = list or {}
if type(src) == 'string' then
for i = 1, src:len() do
list[src:sub(i, i)] = true
end
elseif type(src) == 'table' then
for i = 1, #src do
list[src[i]] = true
end
end
return list
end
--- Base identifier character set.
-- @variable {string} base_ident
local base_ident = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
--- Base identifier character set.
-- @variable {string} base_digits
local base_digits = '0123456789'
--- Base identifier character set.
-- @variable {string} base_operators
local base_operators = '+-*/^%#'
--- Map of Lua character patterns.
-- @table chars
-- @field {table} whitespace Boolean map of whitespace
-- tokens.
-- @field {table} validEscapes Boolean map of valid escape
-- characters.
-- @field {table} ident Boolean map of valid identifier
-- characters.
-- @field {table} symbols Boolean map of valid symbol and
-- operator characters.
-- @local
local chars = {
whitespace = lookupify(' \n\t\r'),
validEscapes = lookupify('abfnrtv"\'\\'),
ident = lookupify(
base_ident .. base_digits,
{
start = lookupify(base_ident),
}
),
digits = lookupify(
base_digits,
{
hex = lookupify(base_digits .. 'abcdefABCDEF')
}
),
symbols = lookupify(
base_operators .. ',{}[]();.:', {
equality = lookupify('~=><'),
operators = lookupify(base_operators)
}
)
}
--- List of Lua keywords.
-- @table keywords
-- @field structure Boolean map of structure keywords.
-- @field values Boolean map of primitive keywords.
local keywords = {
structure = lookupify({
'and', 'break', 'do', 'else', 'elseif', 'end', 'for', 'function',
'goto', 'if', 'in', 'local', 'not', 'or', 'repeat', 'return', 'then',
'until', 'while'
}),
values = lookupify({
'true', 'false', 'nil'
})
}
-- Lexer function export.
return function(text)
local pos = 1
local start = 1
local buffer = {}
local lines = {}
local function look(delta)
delta = pos + (delta or 0)
return text:sub(delta, delta)
end
local function get()
pos = pos + 1
return look(-1)
end
local function getDataLevel()
local num = 0
while look(num) == '=' do
num = num + 1
end
if look(num) == '[' then
pos = pos + num + 1
return num
end
end
local function getCurrentTokenText()
return text:sub(start, pos - 1)
end
local currentLineLength = 0
local lineoffset = 0
local function pushToken(type, text)
text = text or getCurrentTokenText()
local tk = buffer[#buffer]
if not tk or tk.type ~= type then
tk = {
type = type,
data = text,
posFirst = start - lineoffset,
posLast = pos - 1 - lineoffset
}
if tk.data ~= '' then
buffer[#buffer + 1] = tk
end
else
tk.data = tk.data .. text
tk.posLast = tk.posLast + text:len()
end
currentLineLength = currentLineLength + text:len()
start = pos
return tk
end
local function newline()
lines[#lines + 1] = buffer
buffer = {}
get()
pushToken('newline')
buffer[1] = nil
lineoffset = lineoffset + currentLineLength
currentLineLength = 0
end
local function getData(level, type)
while true do
local char = get()
if char == '' then
return
elseif char == '\n' then
pos = pos - 1
pushToken(type)
newline()
elseif char == ']' then
local valid = true
for i = 1, level do
if look() == '=' then
pos = pos + 1
else
valid = false
break
end
end
if valid and look() == ']' then
pos = pos - level - 1
return
end
end
end
end
local function chompWhitespace()
while true do
local char = look()
if char == '\n' then
pushToken('whitespace')
newline()
elseif chars.whitespace[char] then
pos = pos + 1
else
break
end
end
pushToken('whitespace')
end
while true do
chompWhitespace()
local char = get()
if char == '' then
break
elseif char == '-' and look() == '-' then
pos = pos + 1
if look() == '[' then
pos = pos + 1
local level = getDataLevel()
if level then
getData(level, 'comment')
pos = pos + level + 2
pushToken('comment')
else
while true do
local char2 = get()
if char2 == '' or char2 == '\n' then
pos = pos - 1
pushToken('comment')
if char2 == '\n' then
newline()
end
break
end
end
end
else
while true do
local char2 = get()
if char2 == '' or char2 == '\n' then
pos = pos - 1
pushToken('comment')
if char2 == '\n' then
newline()
end
break
end
end
end
pushToken('comment')
elseif char == '\'' or char == '"' then
pushToken('string_start')
while true do
local char2 = get()
if char2 == '\\' then
pos = pos - 1
pushToken('string')
get()
local char3 = get()
if chars.digits[char3] then
for i = 1, 2 do
if chars.digits[look()] then
pos = pos + 1
end
end
elseif char3 == 'x' then
if chars.digits.hex[look()] and chars.digits.hex[look(1)] then
pos = pos + 2
else
pushToken('unidentified')
end
elseif char3 == '\n' then
pos = pos - 1
pushToken('escape')
newline()
elseif not chars.validEscapes[char3] then
pushToken('unidentified')
end
pushToken('escape')
elseif char2 == '\n' then
pos = pos - 1
pushToken('string')
newline()
break
elseif char2 == char or char2 == '' then
pos = pos - 1
pushToken('string')
get()
break
end
end
pushToken('string_end')
elseif chars.ident.start[char] then
while chars.ident[look()] do
pos = pos + 1
end
local word = getCurrentTokenText()
if keywords.structure[word] then
pushToken('keyword')
elseif keywords.values[word] then
pushToken('value')
else
pushToken('ident')
end
elseif chars.digits[char] or (char == '.' and chars.digits[look()]) then
if char == '0' and look() == 'x' then
pos = pos + 1
while chars.digits.hex[look()] do
pos = pos + 1
end
else
while chars.digits[look()] do
pos = pos + 1
end
if look() == '.' then
pos = pos + 1
while chars.digits[look()] do
pos = pos + 1
end
end
if look():lower() == 'e' then
pos = pos + 1
if look() == '-' then
pos = pos + 1
end
while chars.digits[look()] do
pos = pos + 1
end
end
end
pushToken('number')
elseif char == '[' then
local level = getDataLevel()
if level then
pushToken('string_start')
getData(level, 'string')
pushToken('string')
pos = pos + level + 2
pushToken('string_end')
else
pushToken('symbol')
end
elseif char == '.' then
if look() == '.' then
pos = pos + 1
if look() == '.' then
pos = pos + 1
end
end
if getCurrentTokenText():len() == 3 then
pushToken('vararg')
else
pushToken('symbol')
end
elseif char == ':' and look() == ':' then
get()
pushToken('label_start')
chompWhitespace()
if chars.ident.start[look()] then
get()
while chars.ident[look()] do
get()
end
pushToken('label')
chompWhitespace()
if look() == ':' and look(1) == ':' then
get()
get()
pushToken('label_end')
end
end
elseif chars.symbols.equality[char] then
if look() == '=' then
pos = pos + 1
end
pushToken('operator')
elseif chars.symbols[char] then
if chars.symbols.operators[char] then
pushToken('operator')
else
pushToken('symbol')
end
else
pushToken('unidentified')
end
end
lines[#lines + 1] = buffer
return lines
end
kmv09oaxzsoiuyngaiw84n7j6vfbjmq
803407
803406
2026-07-29T23:41:47Z
SM7
3953
1 revision imported from [[:en:Module:Lua_lexer]]
803406
Scribunto
text/plain
--- Lexer for Lua source code written in pure Lua.
-- @script lexer
-- @license [https://github.com/LoganDark/lua-lexer/blob/master/LICENSE MIT]
-- @author https://github.com/LoganDark
-- @param {string} text Lua source code to lex.
-- @return {string} Table of line arrays containing lexemes.
--- Mapper for individual token list string.
-- @param {string} src List of characters or keywords to map.
-- @param[opt] {table} list Table to extend by reference.
-- @return {{char=true,...}}, map
-- @local
local function lookupify(src, list)
list = list or {}
if type(src) == 'string' then
for i = 1, src:len() do
list[src:sub(i, i)] = true
end
elseif type(src) == 'table' then
for i = 1, #src do
list[src[i]] = true
end
end
return list
end
--- Base identifier character set.
-- @variable {string} base_ident
local base_ident = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
--- Base identifier character set.
-- @variable {string} base_digits
local base_digits = '0123456789'
--- Base identifier character set.
-- @variable {string} base_operators
local base_operators = '+-*/^%#'
--- Map of Lua character patterns.
-- @table chars
-- @field {table} whitespace Boolean map of whitespace
-- tokens.
-- @field {table} validEscapes Boolean map of valid escape
-- characters.
-- @field {table} ident Boolean map of valid identifier
-- characters.
-- @field {table} symbols Boolean map of valid symbol and
-- operator characters.
-- @local
local chars = {
whitespace = lookupify(' \n\t\r'),
validEscapes = lookupify('abfnrtv"\'\\'),
ident = lookupify(
base_ident .. base_digits,
{
start = lookupify(base_ident),
}
),
digits = lookupify(
base_digits,
{
hex = lookupify(base_digits .. 'abcdefABCDEF')
}
),
symbols = lookupify(
base_operators .. ',{}[]();.:', {
equality = lookupify('~=><'),
operators = lookupify(base_operators)
}
)
}
--- List of Lua keywords.
-- @table keywords
-- @field structure Boolean map of structure keywords.
-- @field values Boolean map of primitive keywords.
local keywords = {
structure = lookupify({
'and', 'break', 'do', 'else', 'elseif', 'end', 'for', 'function',
'goto', 'if', 'in', 'local', 'not', 'or', 'repeat', 'return', 'then',
'until', 'while'
}),
values = lookupify({
'true', 'false', 'nil'
})
}
-- Lexer function export.
return function(text)
local pos = 1
local start = 1
local buffer = {}
local lines = {}
local function look(delta)
delta = pos + (delta or 0)
return text:sub(delta, delta)
end
local function get()
pos = pos + 1
return look(-1)
end
local function getDataLevel()
local num = 0
while look(num) == '=' do
num = num + 1
end
if look(num) == '[' then
pos = pos + num + 1
return num
end
end
local function getCurrentTokenText()
return text:sub(start, pos - 1)
end
local currentLineLength = 0
local lineoffset = 0
local function pushToken(type, text)
text = text or getCurrentTokenText()
local tk = buffer[#buffer]
if not tk or tk.type ~= type then
tk = {
type = type,
data = text,
posFirst = start - lineoffset,
posLast = pos - 1 - lineoffset
}
if tk.data ~= '' then
buffer[#buffer + 1] = tk
end
else
tk.data = tk.data .. text
tk.posLast = tk.posLast + text:len()
end
currentLineLength = currentLineLength + text:len()
start = pos
return tk
end
local function newline()
lines[#lines + 1] = buffer
buffer = {}
get()
pushToken('newline')
buffer[1] = nil
lineoffset = lineoffset + currentLineLength
currentLineLength = 0
end
local function getData(level, type)
while true do
local char = get()
if char == '' then
return
elseif char == '\n' then
pos = pos - 1
pushToken(type)
newline()
elseif char == ']' then
local valid = true
for i = 1, level do
if look() == '=' then
pos = pos + 1
else
valid = false
break
end
end
if valid and look() == ']' then
pos = pos - level - 1
return
end
end
end
end
local function chompWhitespace()
while true do
local char = look()
if char == '\n' then
pushToken('whitespace')
newline()
elseif chars.whitespace[char] then
pos = pos + 1
else
break
end
end
pushToken('whitespace')
end
while true do
chompWhitespace()
local char = get()
if char == '' then
break
elseif char == '-' and look() == '-' then
pos = pos + 1
if look() == '[' then
pos = pos + 1
local level = getDataLevel()
if level then
getData(level, 'comment')
pos = pos + level + 2
pushToken('comment')
else
while true do
local char2 = get()
if char2 == '' or char2 == '\n' then
pos = pos - 1
pushToken('comment')
if char2 == '\n' then
newline()
end
break
end
end
end
else
while true do
local char2 = get()
if char2 == '' or char2 == '\n' then
pos = pos - 1
pushToken('comment')
if char2 == '\n' then
newline()
end
break
end
end
end
pushToken('comment')
elseif char == '\'' or char == '"' then
pushToken('string_start')
while true do
local char2 = get()
if char2 == '\\' then
pos = pos - 1
pushToken('string')
get()
local char3 = get()
if chars.digits[char3] then
for i = 1, 2 do
if chars.digits[look()] then
pos = pos + 1
end
end
elseif char3 == 'x' then
if chars.digits.hex[look()] and chars.digits.hex[look(1)] then
pos = pos + 2
else
pushToken('unidentified')
end
elseif char3 == '\n' then
pos = pos - 1
pushToken('escape')
newline()
elseif not chars.validEscapes[char3] then
pushToken('unidentified')
end
pushToken('escape')
elseif char2 == '\n' then
pos = pos - 1
pushToken('string')
newline()
break
elseif char2 == char or char2 == '' then
pos = pos - 1
pushToken('string')
get()
break
end
end
pushToken('string_end')
elseif chars.ident.start[char] then
while chars.ident[look()] do
pos = pos + 1
end
local word = getCurrentTokenText()
if keywords.structure[word] then
pushToken('keyword')
elseif keywords.values[word] then
pushToken('value')
else
pushToken('ident')
end
elseif chars.digits[char] or (char == '.' and chars.digits[look()]) then
if char == '0' and look() == 'x' then
pos = pos + 1
while chars.digits.hex[look()] do
pos = pos + 1
end
else
while chars.digits[look()] do
pos = pos + 1
end
if look() == '.' then
pos = pos + 1
while chars.digits[look()] do
pos = pos + 1
end
end
if look():lower() == 'e' then
pos = pos + 1
if look() == '-' then
pos = pos + 1
end
while chars.digits[look()] do
pos = pos + 1
end
end
end
pushToken('number')
elseif char == '[' then
local level = getDataLevel()
if level then
pushToken('string_start')
getData(level, 'string')
pushToken('string')
pos = pos + level + 2
pushToken('string_end')
else
pushToken('symbol')
end
elseif char == '.' then
if look() == '.' then
pos = pos + 1
if look() == '.' then
pos = pos + 1
end
end
if getCurrentTokenText():len() == 3 then
pushToken('vararg')
else
pushToken('symbol')
end
elseif char == ':' and look() == ':' then
get()
pushToken('label_start')
chompWhitespace()
if chars.ident.start[look()] then
get()
while chars.ident[look()] do
get()
end
pushToken('label')
chompWhitespace()
if look() == ':' and look(1) == ':' then
get()
get()
pushToken('label_end')
end
end
elseif chars.symbols.equality[char] then
if look() == '=' then
pos = pos + 1
end
pushToken('operator')
elseif chars.symbols[char] then
if chars.symbols.operators[char] then
pushToken('operator')
else
pushToken('symbol')
end
else
pushToken('unidentified')
end
end
lines[#lines + 1] = buffer
return lines
end
kmv09oaxzsoiuyngaiw84n7j6vfbjmq
Module:Unindent
828
101354
803408
2026-01-30T15:31:11Z
en>Awesome Aasim
0
803408
Scribunto
text/plain
--[[
Copyright (c) 2013 Enrique García Cota
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--]]
-- <nowiki>
--- Unindent resets the indentation level of mulitline strings.
-- It is useful for multiline strings inside functions and large tables.
-- This module serves as a utility function for string parsing.
--
-- Lua supports multiline strings in the format `[[\n...\n]]`. In general,
-- Lua does not outdent indented multiline strings out of the box. Though
-- Lua supports variable indentation in multiline strings, custom logic is
-- necessary to reset the string's indentation. This module adopts a
-- flexible approach based on string scanning.
--
-- Unlike Penlight's `pl.text.dedent` behaviour where every line has the
-- indentation of the first line removed, the line prefixed with the least
-- non-tab whitespace is reset to zero indentation. Thus, the opening line
-- of the string may retain some indentation *if* there are lines of less
-- indentation terminating the string.
--
-- @script unindent
-- @license MIT
-- @release stable
-- @author [[wikia:dev:User:8nml|8nml]]
-- @attribution [https://github.com/kikito @kikito] ([https://github.com/kikito/inspect.lua/blob/master/spec/unindent.lua GitHub])
-- @param {string} str Multiline string indented consistently.
-- @returns {string} Unindented string.
return function(str)
str = str:gsub(' +$', ''):gsub('^ +', '') -- remove spaces at start and end
local level = math.huge
local minPrefix = ''
local len
for prefix in str:gmatch('\n( +)') do
len = #prefix
if len < level then
level = len
minPrefix = prefix
end
end
return (str:gsub('\n' .. minPrefix, '\n'):gsub('\n$', ''))
end
njtcm3z8uvh69ku6zyaxoc681v6dcko
803409
803408
2026-07-29T23:42:02Z
SM7
3953
1 revision imported from [[:en:Module:Unindent]]
803408
Scribunto
text/plain
--[[
Copyright (c) 2013 Enrique García Cota
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--]]
-- <nowiki>
--- Unindent resets the indentation level of mulitline strings.
-- It is useful for multiline strings inside functions and large tables.
-- This module serves as a utility function for string parsing.
--
-- Lua supports multiline strings in the format `[[\n...\n]]`. In general,
-- Lua does not outdent indented multiline strings out of the box. Though
-- Lua supports variable indentation in multiline strings, custom logic is
-- necessary to reset the string's indentation. This module adopts a
-- flexible approach based on string scanning.
--
-- Unlike Penlight's `pl.text.dedent` behaviour where every line has the
-- indentation of the first line removed, the line prefixed with the least
-- non-tab whitespace is reset to zero indentation. Thus, the opening line
-- of the string may retain some indentation *if* there are lines of less
-- indentation terminating the string.
--
-- @script unindent
-- @license MIT
-- @release stable
-- @author [[wikia:dev:User:8nml|8nml]]
-- @attribution [https://github.com/kikito @kikito] ([https://github.com/kikito/inspect.lua/blob/master/spec/unindent.lua GitHub])
-- @param {string} str Multiline string indented consistently.
-- @returns {string} Unindented string.
return function(str)
str = str:gsub(' +$', ''):gsub('^ +', '') -- remove spaces at start and end
local level = math.huge
local minPrefix = ''
local len
for prefix in str:gmatch('\n( +)') do
len = #prefix
if len < level then
level = len
minPrefix = prefix
end
end
return (str:gsub('\n' .. minPrefix, '\n'):gsub('\n$', ''))
end
njtcm3z8uvh69ku6zyaxoc681v6dcko
Module:WikidataIB/nolinks
828
101355
803478
2019-01-01T18:19:33Z
en>RexxS
0
create module to define some common terms which should not be linked.
803478
Scribunto
text/plain
local p ={}
--[[
The values here are the English sitelinks for items that should not be linked.
These 36 are not defininitive and may be altered to suit.
--]]
p.items = {
"Australia",
"Austria",
"Belgium",
"Canada",
"China",
"Denmark",
"England",
"France",
"Germany",
"Greece",
"Hungary",
"Iceland",
"India",
"Republic of Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Luxembourg",
"Mexico",
"Netherlands",
"New Zealand",
"Northern Ireland",
"Norway",
"Poland",
"Portugal",
"Russia",
"Scotland",
"South Africa",
"Spain",
"Sweden",
"Switzerland",
"Turkey",
"United Kingdom",
"United States",
"Wales",
}
--[[
This provides a convenient way to create a test whether an item is on the list.
--]]
p.itemsindex = {}
for i, v in ipairs(items) do
itemsindex[v] = true
end
return p
avfgdf3p4rlq80n3vttiwse2rc8o5jl
803479
803478
2019-01-01T18:49:12Z
en>RexxS
0
p.itemsindex and p.items
803479
Scribunto
text/plain
local p ={}
--[[
The values here are the English sitelinks for items that should not be linked.
These 36 are not defininitive and may be altered to suit.
--]]
p.items = {
"Australia",
"Austria",
"Belgium",
"Canada",
"China",
"Denmark",
"England",
"France",
"Germany",
"Greece",
"Hungary",
"Iceland",
"India",
"Republic of Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Luxembourg",
"Mexico",
"Netherlands",
"New Zealand",
"Northern Ireland",
"Norway",
"Poland",
"Portugal",
"Russia",
"Scotland",
"South Africa",
"Spain",
"Sweden",
"Switzerland",
"Turkey",
"United Kingdom",
"United States",
"Wales",
}
--[[
This provides a convenient way to create a test whether an item is on the list.
--]]
p.itemsindex = {}
for i, v in ipairs(p.items) do
p.itemsindex[v] = true
end
return p
k76cymw995xwvtr574mvyq05caggbdc
803480
803479
2019-01-07T01:22:30Z
71.163.7.214
803480
Scribunto
text/plain
local p ={}
--[[
The values here are the English sitelinks for items that should not be linked.
These 36 are not defininitive and may be altered to suit.
--]]
p.items = {
"Austral",
"Austria",
"Belgium",
"Canada",
"China",
"Denmark",
"England",
"France",
"Germany",
"Greece",
"Hungary",
"Iceland",
"India",
"Republic of Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Luxembourg",
"Mexico",
"Netherlands",
"New Zealand",
"Northern Ireland",
"Norway",
"Poland",
"Portugal",
"Russia",
"Scotland",
"South Africa",
"Spain",
"Sweden",
"Switzerland",
"Turkey",
"United Kingdom",
"United States",
"Wales",
}
--[[
This provides a convenient way to create a test whether an item is on the list.
--]]
p.itemsindex = {}
for i, v in ipairs(p.items) do
p.itemsindex[v] = true
end
return p
c060vtph4yywz2noowizhotdxl1oqma
803481
803480
2019-01-07T02:44:02Z
en>RexxS
0
Reverted edits by [[Special:Contribs/71.163.7.214|71.163.7.214]] ([[User talk:71.163.7.214|talk]]) to last version by RexxS
803481
Scribunto
text/plain
local p ={}
--[[
The values here are the English sitelinks for items that should not be linked.
These 36 are not defininitive and may be altered to suit.
--]]
p.items = {
"Australia",
"Austria",
"Belgium",
"Canada",
"China",
"Denmark",
"England",
"France",
"Germany",
"Greece",
"Hungary",
"Iceland",
"India",
"Republic of Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Luxembourg",
"Mexico",
"Netherlands",
"New Zealand",
"Northern Ireland",
"Norway",
"Poland",
"Portugal",
"Russia",
"Scotland",
"South Africa",
"Spain",
"Sweden",
"Switzerland",
"Turkey",
"United Kingdom",
"United States",
"Wales",
}
--[[
This provides a convenient way to create a test whether an item is on the list.
--]]
p.itemsindex = {}
for i, v in ipairs(p.items) do
p.itemsindex[v] = true
end
return p
k76cymw995xwvtr574mvyq05caggbdc
803482
803481
2019-01-07T02:45:15Z
en>RexxS
0
sp
803482
Scribunto
text/plain
local p ={}
--[[
The values here are the English sitelinks for items that should not be linked.
These 36 are not definitive and may be altered to suit.
--]]
p.items = {
"Australia",
"Austria",
"Belgium",
"Canada",
"China",
"Denmark",
"England",
"France",
"Germany",
"Greece",
"Hungary",
"Iceland",
"India",
"Republic of Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Luxembourg",
"Mexico",
"Netherlands",
"New Zealand",
"Northern Ireland",
"Norway",
"Poland",
"Portugal",
"Russia",
"Scotland",
"South Africa",
"Spain",
"Sweden",
"Switzerland",
"Turkey",
"United Kingdom",
"United States",
"Wales",
}
--[[
This provides a convenient way to create a test whether an item is on the list.
--]]
p.itemsindex = {}
for i, v in ipairs(p.items) do
p.itemsindex[v] = true
end
return p
i1pgsfuagq0i2dmjnfb0y02n1hiitd0
803483
803482
2019-01-07T03:17:02Z
en>Ad Orientem
0
Protected "[[Module:WikidataIB/nolinks]]": High-risk Lua module: conforming protection to [[Module:WikidataIB]] and allow extended confirmed editors to edit. Per request at RfPP ([Edit=Require extended confirmed access] (indefinite) [Move=Require extended confirmed access] (indefinite))
803482
Scribunto
text/plain
local p ={}
--[[
The values here are the English sitelinks for items that should not be linked.
These 36 are not definitive and may be altered to suit.
--]]
p.items = {
"Australia",
"Austria",
"Belgium",
"Canada",
"China",
"Denmark",
"England",
"France",
"Germany",
"Greece",
"Hungary",
"Iceland",
"India",
"Republic of Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Luxembourg",
"Mexico",
"Netherlands",
"New Zealand",
"Northern Ireland",
"Norway",
"Poland",
"Portugal",
"Russia",
"Scotland",
"South Africa",
"Spain",
"Sweden",
"Switzerland",
"Turkey",
"United Kingdom",
"United States",
"Wales",
}
--[[
This provides a convenient way to create a test whether an item is on the list.
--]]
p.itemsindex = {}
for i, v in ipairs(p.items) do
p.itemsindex[v] = true
end
return p
i1pgsfuagq0i2dmjnfb0y02n1hiitd0
803484
803483
2019-01-24T14:07:29Z
en>Galobtter
0
Changed protection level for "[[Module:WikidataIB/nolinks]]": [[WP:High-risk templates|High-risk Lua module]]: 600000+ transclusions ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
803482
Scribunto
text/plain
local p ={}
--[[
The values here are the English sitelinks for items that should not be linked.
These 36 are not definitive and may be altered to suit.
--]]
p.items = {
"Australia",
"Austria",
"Belgium",
"Canada",
"China",
"Denmark",
"England",
"France",
"Germany",
"Greece",
"Hungary",
"Iceland",
"India",
"Republic of Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Luxembourg",
"Mexico",
"Netherlands",
"New Zealand",
"Northern Ireland",
"Norway",
"Poland",
"Portugal",
"Russia",
"Scotland",
"South Africa",
"Spain",
"Sweden",
"Switzerland",
"Turkey",
"United Kingdom",
"United States",
"Wales",
}
--[[
This provides a convenient way to create a test whether an item is on the list.
--]]
p.itemsindex = {}
for i, v in ipairs(p.items) do
p.itemsindex[v] = true
end
return p
i1pgsfuagq0i2dmjnfb0y02n1hiitd0
803485
803484
2019-01-31T16:27:33Z
en>RexxS
0
add abbreviations UK and USA
803485
Scribunto
text/plain
local p ={}
--[[
The values here are the English sitelinks for items that should not be linked.
These 36 are not definitive and may be altered to suit.
--]]
p.items = {
"Australia",
"Austria",
"Belgium",
"Canada",
"China",
"Denmark",
"England",
"France",
"Germany",
"Greece",
"Hungary",
"Iceland",
"India",
"Republic of Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Luxembourg",
"Mexico",
"Netherlands",
"New Zealand",
"Northern Ireland",
"Norway",
"Poland",
"Portugal",
"Russia",
"Scotland",
"South Africa",
"Spain",
"Sweden",
"Switzerland",
"Turkey",
"United Kingdom",
"UK",
"United States",
"USA",
"Wales",
}
--[[
This provides a convenient way to create a test whether an item is on the list.
--]]
p.itemsindex = {}
for i, v in ipairs(p.items) do
p.itemsindex[v] = true
end
return p
os706fxg7ivfr0wq2o2z1afni11n93q
803486
803485
2026-07-30T00:15:03Z
SM7
3953
8 revisions imported from [[:en:Module:WikidataIB/nolinks]]
803485
Scribunto
text/plain
local p ={}
--[[
The values here are the English sitelinks for items that should not be linked.
These 36 are not definitive and may be altered to suit.
--]]
p.items = {
"Australia",
"Austria",
"Belgium",
"Canada",
"China",
"Denmark",
"England",
"France",
"Germany",
"Greece",
"Hungary",
"Iceland",
"India",
"Republic of Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Luxembourg",
"Mexico",
"Netherlands",
"New Zealand",
"Northern Ireland",
"Norway",
"Poland",
"Portugal",
"Russia",
"Scotland",
"South Africa",
"Spain",
"Sweden",
"Switzerland",
"Turkey",
"United Kingdom",
"UK",
"United States",
"USA",
"Wales",
}
--[[
This provides a convenient way to create a test whether an item is on the list.
--]]
p.itemsindex = {}
for i, v in ipairs(p.items) do
p.itemsindex[v] = true
end
return p
os706fxg7ivfr0wq2o2z1afni11n93q
Module:WikidataIB/titleformats
828
101356
803487
2019-09-15T16:39:21Z
en>RexxS
0
Creating Module:WikidataIB/titleformats
803487
Scribunto
text/plain
--[[
To satisfy Wikipedia:Manual of Style/Titles, certain types of items are italicised,
and others are quoted.
This submodule lists the entity-ids used in 'instance of' (P31),
which allows a module to identify the values that should be formatted.
The table p.formats is indexed by entity-id, and contains the value " or ''
--]]
local p = {}
p.italics = {
Q571, -- book
Q13593966, -- literary trilogy
Q277759, -- book series
Q2188189, -- musical work
Q11424, -- film
Q13593818, -- film trilogy
Q24856, -- film series
Q5398426, -- television series
Q482994, -- album
Q169930, -- extended play
Q1760610, -- comic book
Q7889, -- video game
Q7058673, -- video game series
Q25379, -- play
Q2743, -- musical
Q37484, -- epic poem
}
p.quotes = {
Q207628, -- musical composition
}
p.formats = {}
for idx, val in ipairs(p.italics) do
p.formats[val] = '"'
end
for idx, val in ipairs(p.quotes) do
p.formats[val] = "''"
end
return p
9wfj48pnl0jtmf6yr189wo4gevlnbbz
803488
803487
2019-09-15T17:31:59Z
en>RexxS
0
right way round
803488
Scribunto
text/plain
--[[
To satisfy Wikipedia:Manual of Style/Titles, certain types of items are italicised,
and others are quoted.
This submodule lists the entity-ids used in 'instance of' (P31),
which allows a module to identify the values that should be formatted.
The table p.formats is indexed by entity-id, and contains the value " or ''
--]]
local p = {}
p.italics = {
Q571, -- book
Q13593966, -- literary trilogy
Q277759, -- book series
Q2188189, -- musical work
Q11424, -- film
Q13593818, -- film trilogy
Q24856, -- film series
Q5398426, -- television series
Q482994, -- album
Q169930, -- extended play
Q1760610, -- comic book
Q7889, -- video game
Q7058673, -- video game series
Q25379, -- play
Q2743, -- musical
Q37484, -- epic poem
}
p.quotes = {
Q207628, -- musical composition
}
p.formats = {}
for idx, val in ipairs(p.italics) do
p.formats[val] = "''"
end
for idx, val in ipairs(p.quotes) do
p.formats[val] = '"'
end
return p
b05fxq9kaj3vbgor458s2m9pavbtbd6
803489
803488
2019-09-15T17:58:06Z
en>RexxS
0
return size of formats table
803489
Scribunto
text/plain
--[[
To satisfy Wikipedia:Manual of Style/Titles, certain types of items are italicised,
and others are quoted.
This submodule lists the entity-ids used in 'instance of' (P31),
which allows a module to identify the values that should be formatted.
The table p.formats is indexed by entity-id, and contains the value " or ''
--]]
local p = {}
p.italics = {
Q571, -- book
Q13593966, -- literary trilogy
Q277759, -- book series
Q2188189, -- musical work
Q11424, -- film
Q13593818, -- film trilogy
Q24856, -- film series
Q5398426, -- television series
Q482994, -- album
Q169930, -- extended play
Q1760610, -- comic book
Q7889, -- video game
Q7058673, -- video game series
Q25379, -- play
Q2743, -- musical
Q37484, -- epic poem
}
p.quotes = {
Q207628, -- musical composition
}
p.size = 0
p.formats = {}
for i, v in ipairs(p.italics) do
p.formats[v] = "''"
p.size = p.size + 1
end
for i, v in ipairs(p.quotes) do
p.formats[v] = '"'
p.size = p.size + 1
end
return p
i0fwqntli62pdtn0w2c5bvigcwsncma
803490
803489
2019-09-15T18:04:45Z
en>RexxS
0
strings
803490
Scribunto
text/plain
--[[
To satisfy Wikipedia:Manual of Style/Titles, certain types of items are italicised,
and others are quoted.
This submodule lists the entity-ids used in 'instance of' (P31),
which allows a module to identify the values that should be formatted.
The table p.formats is indexed by entity-id, and contains the value " or ''
--]]
local p = {}
p.italics = {
"Q571", -- book
"Q13593966", -- literary trilogy
"Q277759", -- book series
"Q2188189", -- musical work
"Q11424", -- film
"Q13593818", -- film trilogy
"Q24856", -- film series
"Q5398426", -- television series
"Q482994", -- album
"Q169930", -- extended play
"Q1760610", -- comic book
"Q7889", -- video game
"Q7058673", -- video game series
"Q25379", -- play
"Q2743", -- musical
"Q37484", -- epic poem
}
p.quotes = {
"Q207628", -- musical composition
}
p.size = 0
p.formats = {}
for i, v in ipairs(p.italics) do
p.formats[v] = "''"
p.size = p.size + 1
end
for i, v in ipairs(p.quotes) do
p.formats[v] = '"'
p.size = p.size + 1
end
return p
ceo95yqn1m1wfaizrph32yqgl164ul4
803491
803490
2019-09-25T18:01:12Z
en>MusikBot II
0
Protected "[[Module:WikidataIB/titleformats]]": [[Wikipedia:High-risk templates|High-risk template or module]] ([[User:MusikBot II/TemplateProtector|more info]]) ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
803490
Scribunto
text/plain
--[[
To satisfy Wikipedia:Manual of Style/Titles, certain types of items are italicised,
and others are quoted.
This submodule lists the entity-ids used in 'instance of' (P31),
which allows a module to identify the values that should be formatted.
The table p.formats is indexed by entity-id, and contains the value " or ''
--]]
local p = {}
p.italics = {
"Q571", -- book
"Q13593966", -- literary trilogy
"Q277759", -- book series
"Q2188189", -- musical work
"Q11424", -- film
"Q13593818", -- film trilogy
"Q24856", -- film series
"Q5398426", -- television series
"Q482994", -- album
"Q169930", -- extended play
"Q1760610", -- comic book
"Q7889", -- video game
"Q7058673", -- video game series
"Q25379", -- play
"Q2743", -- musical
"Q37484", -- epic poem
}
p.quotes = {
"Q207628", -- musical composition
}
p.size = 0
p.formats = {}
for i, v in ipairs(p.italics) do
p.formats[v] = "''"
p.size = p.size + 1
end
for i, v in ipairs(p.quotes) do
p.formats[v] = '"'
p.size = p.size + 1
end
return p
ceo95yqn1m1wfaizrph32yqgl164ul4
803492
803491
2020-04-30T16:11:19Z
en>RexxS
0
add magazine
803492
Scribunto
text/plain
--[[
To satisfy Wikipedia:Manual of Style/Titles, certain types of items are italicised,
and others are quoted.
This submodule lists the entity-ids used in 'instance of' (P31),
which allows a module to identify the values that should be formatted.
The table p.formats is indexed by entity-id, and contains the value " or ''
--]]
local p = {}
p.italics = {
"Q571", -- book
"Q13593966", -- literary trilogy
"Q277759", -- book series
"Q2188189", -- musical work
"Q11424", -- film
"Q13593818", -- film trilogy
"Q24856", -- film series
"Q5398426", -- television series
"Q482994", -- album
"Q169930", -- extended play
"Q1760610", -- comic book
"Q7889", -- video game
"Q7058673", -- video game series
"Q25379", -- play
"Q2743", -- musical
"Q37484", -- epic poem
"Q41298", -- magazine
}
p.quotes = {
"Q207628", -- musical composition
}
p.size = 0
p.formats = {}
for i, v in ipairs(p.italics) do
p.formats[v] = "''"
p.size = p.size + 1
end
for i, v in ipairs(p.quotes) do
p.formats[v] = '"'
p.size = p.size + 1
end
return p
kf27lmax414v7k61jzza3jomkstxn1m
803493
803492
2026-07-30T00:15:17Z
SM7
3953
6 revisions imported from [[:en:Module:WikidataIB/titleformats]]
803492
Scribunto
text/plain
--[[
To satisfy Wikipedia:Manual of Style/Titles, certain types of items are italicised,
and others are quoted.
This submodule lists the entity-ids used in 'instance of' (P31),
which allows a module to identify the values that should be formatted.
The table p.formats is indexed by entity-id, and contains the value " or ''
--]]
local p = {}
p.italics = {
"Q571", -- book
"Q13593966", -- literary trilogy
"Q277759", -- book series
"Q2188189", -- musical work
"Q11424", -- film
"Q13593818", -- film trilogy
"Q24856", -- film series
"Q5398426", -- television series
"Q482994", -- album
"Q169930", -- extended play
"Q1760610", -- comic book
"Q7889", -- video game
"Q7058673", -- video game series
"Q25379", -- play
"Q2743", -- musical
"Q37484", -- epic poem
"Q41298", -- magazine
}
p.quotes = {
"Q207628", -- musical composition
}
p.size = 0
p.formats = {}
for i, v in ipairs(p.italics) do
p.formats[v] = "''"
p.size = p.size + 1
end
for i, v in ipairs(p.quotes) do
p.formats[v] = '"'
p.size = p.size + 1
end
return p
kf27lmax414v7k61jzza3jomkstxn1m
बोरिस जोर्जेविच परचात्किन
0
101357
803655
2026-07-30T06:42:06Z
Asorev
38000
पन्ना बनावल गइल "{{Infobox person | name = बोरिस परचात्किन | native_name = Борис Георгиевич Перчаткин | image = | caption = | birth_date = {{Birth date and age|1946|7|1}} | birth_place = [[मिर्होरौद]], यूक्रेनी सोवियत समाजवादी गणराज्य|यूक्रेनी..." के साथ
803655
wikitext
text/x-wiki
{{Infobox person
| name = बोरिस परचात्किन
| native_name = Борис Георгиевич Перчаткин
| image =
| caption =
| birth_date = {{Birth date and age|1946|7|1}}
| birth_place = [[मिर्होरौद]], [[यूक्रेनी सोवियत समाजवादी गणराज्य|यूक्रेनी SSR]], [[सोवियत संघ]]<ref name="ostrovskaja_68">{{cite journal |last=Островская |first=О. П. |year=2017 |title=Судьбы участников эмиграционного движения пятидесятников на дальнем востоке в 1970-1980-х гг. |journal=Владивосток – точка возвращения: прошлое и настоящее русской эмиграции |publisher=Дальневосточный юридический институт МВД РФ |url=http://dot.kostacademy.kz/bible/files/769476832.pdf#page=65 |language=ru |pages=68}}</ref>
| occupation = मानवाधिकार कार्यकर्ता, धार्मिक नेता
| children = 6<ref name="ostrovskaja_68" />
}}
'''बोरिस जोर्जेविच परचात्किन''' (रूसी: ''Борис Георгиевич Перчаткин''; जनम: 1 जुलाई 1946) सोवियत संघ आ अमेरिका के एकठु मानवाधिकार कार्यकर्ता हउवँ आ 1970 के दशक के अंत आ 1980 के दशक के शुरुआत में [[नाखोदका]] शहर से शुरू भइल धार्मिक प्रवास आंदोलन के प्रमुख नेता रहलें<ref name="ostrovskaja_68" />। 1989 में उहाँ [[अमेरिका|संयुक्त राज्य अमेरिका]] में "लाउतेनबर्ग संशोधन" (Lautenberg Amendment) के पास करावे खातिर पैरवी के नेतृत्व कइलीं, जेकरा चलते पूर्व सोवियत संघ के देसवन से लगभग 10 लाख धार्मिक अल्पसंख्यकलोग कानूनी रूप से अमेरिका में जा के बस सकल<ref name="romanovic">{{cite web |last=Романович |first=Михаил |year=2023 |title=Правозащитник, Борис Перчаткин, смертельно болен |website=spasenie.by |date=2023-09-26 |url=https://spasenie.by/publ/pravozashhitnik_boris_perchatkin_smertelno_bolen/4-1-0-82 |language=ru}}</ref>।
== जीवनी ==
=== सुरुआती जीवन आ चर्च में शामिल भइल ===
बोरिस परचात्किन के जनम एक सैन्य पायलट के परिवार में भइल रहे। बोरिस के जनम के कुछ समय बाद ही उनकर पिता के एक कार दुर्घटना में मौत हो गइल। कुछ साल बाद उनकर माई दूसरा सैन्यकर्मी से बियाह क लेली। लेकिन 1953 में उनकर सौतेला बाप के विद्रोह के दौरान हुक्म ना माने के कारण गिरफ्तार क लिहल गइल आ [[वोरकुता]] के जबरन श्रम शिविर में 25 साल के सजा सुनावल गइल<ref name="ostrovskaja_68" />। 16 साल के उमिर में बोरिस नाखोदका में पेन्टेकोस्टल चर्च के खोज कइलें आ ओकर सदस्य बन गइलें। ओकरा बाद उहाँ सोवियत संघ में धार्मिक लोगन पर भइल अत्याचार के बारे में जानकारी जुटावे लगलें आ बिदेसी पत्रकारन से शेयर करे लगलें<ref name="semjonova">{{cite web |last=Семёнова |first=Виктория |year=2020 |title=Борис Перчаткин — о том, как помогал верующим из СССР попасть в США |website=usa.one |date=2020-01-03 |url=https://usa.one/2020/01/boris-perchatkin-o-veruyushhix-v-sssr-immigracii-v-ssha-i-aktivizme/ |language=ru}}</ref>।
=== प्रवास आंदोलन आ असंतुष्टन से संबंध ===
1976 में परचात्किन अपने साथी वासिली पात्रुशेव, वी. ए. बुरलाचेन्को आ व्लादिमीर स्टेपानोव के साथे [[मॉस्को]] में सोवियत असंतुष्टन (dissidents) से संपर्क साधलें<ref name="ostrovskaja_68_69">{{cite journal |last=Островская |first=О. П. |year=2017 |title=Судьбы участников эмиграционного движения пятидесятников на дальнем востоке в 1970-1980-х гг. |journal=Владивосток – точка возвращения: прошлое и настоящее русской эмиграции |publisher=Дальневосточный юридический институт МВД РФ |url=http://dot.kostacademy.kz/bible/files/769476832.pdf#page=65 |language=ru |pages=68—69}}</ref>। असंतुष्टलोग सलाह दिहल कि बढ़त प्रवास आंदोलन के एक संगठित रूप दिहल जाव<ref name="ostrovskaja_68_69" />। ओही साल [[मॉस्को हेलसिंकी ग्रुप]] के प्रतिनिधि एल. वी. वोरोनिना नाखोदका आइली<ref name="ostrovskaja_68_69" />। ओकरा बाद से परचात्किन पेन्टेकोस्टल समुदाय के सोवियत संघ से सामूहिक निकास खातिर सक्रिय रूप से काम कइलें<ref name="ostrovskaja_68_69" />। उहाँ [[संयुक्त राष्ट्र]], [[चर्चन के विश्व परिषद]] (WCC) आ [[हेलसिंकी समझौता]] पर दस्तखत करे वाला देसन के प्रमुखन के अपील भेज के सोवियत संघ में धार्मिक उत्पीड़न के जानकारी दिहलीं<ref name="nashaamerica">{{cite web|url=http://www.nashaamerica.com/blogi/chtivo/item-894/|title=Он спас 1 млн человек. Сегодня они не могут собрать ему на операцию. Как же так?|website=www.nashaamerica.com|date=2023-09-26|access-date=2023-09-26|language=ru|archive-date=2023-09-26|archive-url=https://web.archive.org/web/20230926133215/http://www.nashaamerica.com/blogi/chtivo/item-894/|url-status=live}}</ref>।
1977 में निकोलाई गोरेती के साथे पेन्टेकोस्टल समुदाय के ओर से गिरफ्तार असंतुष्टन [[अलेक्जेंडर गिंज़बर्ग]], [[युरी ओर्लोव]] आ [[अनातोली शचरान्स्की]] के बचाव कइलें<ref name="ostrovskaja_66">{{cite journal |last=Островская |first=О. П. |year=2017 |title=Судьбы участников эмиграционного движения пятидесятников на дальнем востоке в 1970-1980-х гг. |journal=Владивосток – точка возвращения: прошлое и настоящее русской эмиграции |publisher=Дальневосточный юридический институт МВД РФ |url=http://dot.kostacademy.kz/bible/files/769476832.pdf#page=65 |language=ru |pages=66}}</ref>। ओही साल बाकी ईसाई पंथन के साथ मिलकर उत्पीड़ित ईसाइयन खातिर देश छोड़े के इजाजत माँगे खातिर सार्वजनिक भूख हड़ताल में भाग लिहलें<ref name="ukrweekly">{{cite journal|author=Zenon Snylyk|title=Dissidents fast across USSR|url=https://archive.ukrweekly.com/print-media/1977/The_Ukrainian_Weekly_1977-38.pdf|journal=The Ukrainian Weekly|publisher=Ukrainian National Association|date=1977-10-09|volume=LXXXIV|issue=222|pages=2|language=en|archive-date=2023-09-26|archive-url=https://web.archive.org/web/20230926074510/https://archive.ukrweekly.com/print-media/1977/The_Ukrainian_Weekly_1977-38.pdf|url-status=live}}</ref>।
20 दिसंबर 1978 के परचात्किन नाखोदका के डाकघर से अमेरिकी राष्ट्रपति [[जिमी कार्टर]] के नया साल के टेलीग्राम भेजे के कोशिश कइलें, जेमे धार्मिक आजादी से वंचित लोगन पर ध्यान देवे के बिनती कइल गइल रहे<ref name="ostrovskaja_69">{{cite journal |last=Островская |first=О. П. |year=2017 |title=Судьбы участников эмиграционного движения пятидесятников на дальнем востоке в 1970-1980-х гг. |journal=Владивосток – точка возвращения: прошлое и настоящее русской эмиграции |publisher=Дальневосточный юридический институт МВД РФ |url=http://dot.kostacademy.kz/bible/files/769476832.pdf#page=65 |language=ru |pages=69}}</ref><ref name="nashaamerica"/><ref name="dementjev">{{cite book |author=Андрей Дементьев |title=АВЕН-ЕЗЕР. Евангельское движение в Приморье 1898-1990 годы |url=http://www.rusbaptist.stunda.org/aven-ezer-primorje.pdf |location=Vladivostok |publisher=Русский остров |year=2011 |pages=213 |isbn=978-5-93577-054-2 |language=ru |archive-date=2023-09-25 |archive-url=https://web.archive.org/web/20230925212100/http://www.rusbaptist.stunda.org/aven-ezer-primorje.pdf |url-status=live}}</ref>।
1979 से उहाँ देस छोड़े के इच्छुक लोगन खातिर एक केंद्र बनाए के संगठनात्मक काम के नेतृत्व कइलें। 1980 में उहाँ के नवगठित "सोवियत पेन्टेकोस्टल चर्च काउंसिल फॉर इमिग्रेशन" के सचिव चुनल गइल<ref name="ostrovskaja_69" />।
=== सरकारी उत्पीड़न आ जेल ===
प्रवास समिति के सबसे सक्रिय सदस्य भइला के कारण उहाँ सोवियत अधिकारियों के निशाना बन गइलें<ref name="ostrovskaja_69" />। स्थानीय मीडिया में उनुका आ उनकर परिवार के खिलाफ बदनामी के अभियान चलावल गइल<ref name="ostrovskaja_69" />।
21 अगस्त 1980 के परचात्किन के नाखोदका में गिरफ्तार क लिहल गइल<ref name="ostrovskaja_69" />। मार्च 1981 के अंत में<ref name="dtic">[https://apps.dtic.mil/sti/pdfs/ADA145453.pdf Chronology of Soviet Dissidence: January 1970 through December 1982]</ref>, आरएसएफएसआर आपराधिक संहिता के अनुच्छेद 190.1 ("सोवियत राज्य आ सामाजिक व्यवस्था के बदनाम करे वाला झूठ फइलावे") के तहत उनुका के 2 साल के जेल के सजा सुनावल गइल<ref name="dtic"/>। [[वॉइस ऑफ अमेरिका]] के रिपोर्ट के मुताबिक, पूछताछ आ ट्रायल के दौरान उनुका के न्यूरोलेप्टिक रसायन दिहल गइल जेकरा से उहाँ कई बेर बेहोश हो गइलें<ref name="ostrovskaja_69" />।
अगस्त 1982 में रिहा भइला के बाद उहाँ देश छोड़े के अधिकार खातिर लड़ाई जारी रखलें। लेकिन फरवरी 1983 में उनुका के दोबारा गिरफ्तार क लिहल गइल आ अवैध हथियार रखे के झूठा आरोप में 1.5 साल के जेल के सजा दिहल गइल<ref name="ostrovskaja_69" />।
=== अमेरिका में प्रवास आ लाउतेनबर्ग संशोधन ===
1987 में उहाँ अमेरिकी कांग्रेस खातिर "सोवियत संघ में धर्म के स्थिति" पर एक रिपोर्ट तैयार कइलें। ई रिपोर्ट कांग्रेस में पढ़ल गइल आ परचात्किन के अमेरिका के विदेश मंत्री [[जॉर्ज शुल्त्ज़]]<ref name="romanovic"/> आ मुख्य न्यायाधीश [[विलियम रेनक्विस्ट]]<ref name="romanovic"/> से मिले खातिर बुलावल गइल। मई 1988 में जब अमेरिकी राष्ट्रपति [[रोनाल्ड रीगन]] आ सोवियत नेता [[मिखाइल गोर्बाचेव]] मॉस्को में शिखर सम्मेलन कइलें, तब परचात्किन के सोवियत प्रोटेस्टेंट प्रतिनिधि के रूप में फर्स्ट लेडी [[नैंसी रीगन]] से मिले खातिर 40 लोगन के प्रतिनिधिमंडल में आमंत्रित कइल गइल रहे<ref name="gura_173">{{cite journal |last=Гура |first=В. О. |year=2018 |title=Історія та есхатологічні мотиви релігійної еміграції пізніх протестантів із СРСР 1987–2017 рр. |journal=Гілея: науковий вісник |issue=135 |pages=171—175 |issn=2076-1554 |url=http://www.irbis-nbuv.gov.ua/cgi-bin/irbis_nbuv/cgiirbis_64.exe?C21COM=2&I21DBN=UJRN&P21DBN=UJRN&IMAGE_FILE_DOWNLOAD=1&Image_file_name=PDF/gileya_2018_135_47.pdf |language=uk}}</ref>। ओह बैठक में उहाँ धार्मिक आजादी आ बिना कौनों बाधा के देस छोड़े के अधिकार खातिर अपील सउंपलें।
26 जुलाई 1988 के परचात्किन अपना पूरा परिवार के साथ अमेरिका चले गइलें<ref name="gura_173" /><ref name="ostrovskaja_69" />। 4 अगस्त 1988 के उहाँ अमेरिकी कांग्रेस में सोवियत संघ में धार्मिक उत्पीड़न पर भाषण दिहले<ref name="gura_173" /><ref name="ostrovskaja_69" /><ref name="photo">{{cite web|url=https://russiainphoto.ru/photos/195013/|title=Foto - Boris Perchatkin|website=russiainphoto.ru|date=2023-09-26|language=ru}}</ref>। एही प्रयासन के बदौलत<ref name="futornyj">{{cite journal|author=Виталий Футорный|title=25-летие начала исхода|url=https://issuu.com/afisha/docs/diaspora_08-11-13/9|journal=Диаспора. Славянская общественная газета|date=2013-08-11|volume=16|issue=15|pages=9|language=ru}}</ref> 1989 में "लाउतेनबर्ग संशोधन" पास भइल, जेकर नाम [[न्यू जर्सी]] के सीनेटर [[फ्रैंक लाउतेनबर्ग]] के नाम पर रखल गइल रहे। एह कानून के पहिला 10 साल में लगभग 3,50,000 धार्मिक शरणार्थी अमेरिका में बसलें<ref name="gura_173" /><ref name="semjonova" />, आ 2010 तक ई संख्या लगभग 10 लाख तक पहुँच गइल<ref name="romanovic"/>।
=== अमेरिका में मानवाधिकार गतिविधि ===
अमेरिका में रहत समय परचात्किन अपना जेल आ संघर्ष के जीवन पर एक वेबसाइट शुरू कइलें आ एक ईसाई मानवाधिकार संगठन ARRC (American-Russian Relief Committee) के स्थापना कइलें<ref name="ostrovskaja_69" /><ref name="romanovic"/><ref name="arrc">{{cite book|author=United States Congress Commission on Security and Cooperation in Europe|title=Unregistered Religious Groups in Russia: Hearing Before the Commission on Security and Cooperation in Europe|url=https://books.google.com/books?id=_YhIiVBx_5kC|year=2007|publisher=U.S. Government Printing Office|isbn=978-0-16-077910-7|language=en}}</ref>। उहाँ अपना आत्मकथात्मक किताब "आग के रास्ते" (रूसी: ''Огненные тропы'') प्रकाशित कइलें<ref name="ostrovskaja_69" />।
2025 के गरमी में परचात्किन आ उनकर समर्थक अमेरिकी विदेश विभाग आ अंतर्राष्ट्रीय धार्मिक स्वतंत्रता पर अमेरिकी आयोग (USCIRF) के सामने गवाही दिहलें, जेमे यूक्रेन आ रूस में शांतिवादी ईसाइयन के उत्पीड़न आ जबरन सेना में भर्ती के मुद्दा उठावल गइल। एह सिलसिले में उहाँ [[Uniting for Ukraine|U4U]] कार्यक्रम के तहत अमेरिकी आवे वाला यूक्रेनी शांतिवादी ईसाइयन खातिर विशेष कोटा खातिर पैरवी कइलें<ref name="slavicsac">{{cite web|url=https://www.slavicsac.com/2025/07/14/ukraine-russian-christians/|title=В США обсуждают защиту христиан из Украины и России|author=SlavicSac|website=Slavic Sacramento|date=2025-07-15|language=ru}}</ref>।
== सम्मान आ उल्लेख ==
प्रसिद्ध सोवियत असंतुष्ट [[आंद्रेई साखारोव]] 1981 में लिखे गइल अपना निबंध "वैज्ञानिकन के जिम्मेदारी" में राजनीतिक बंदियों के सूची में बोरिस परचात्किन के नाम के विशेष रूप से उल्लेख कइले रहलें<ref name="sakharov">{{cite web|url=https://www.sakharov.space/lib/otvetstvennost-uchenyh|title=Сахаров. Ответственность учёных|website=sakharov.space|language=ru}}</ref>।
== संदर्भ ==
<references />
== बाहरी कड़ी ==
* {{Cite journal
|author = Michael Rowe
|title = The Soviet pentecostal emigration movement
|url = https://www.tandfonline.com/doi/epdf/10.1080/09637498308431098
|journal = Religion in Communist Lands
|language = en
|date = 2 January 2008
|volume = 11
|issue = 3
|pages = 337–339
|doi = 10.1080/09637498308431098
|ref = Rowe
|url-access= subscription }}
* {{Cite journal
|author = Catherine Wanner
|title = Religion and Refugee Resettlement: Evolving Connections to Ukraine since World War II
|url = https://brill.com/view/journals/css/44/1-2/article-p44_4.xml
|journal = Canadian-American Slavic Studies
|language = en
|publisher = Brill Schöningh
|date = January 2010
|volume = 44
|issue = 1–2
|pages = 44–66
|issn = 2210-2396
|doi = 10.1163/221023910X512796
|ref = Wanner
|url-access= subscription}}
* {{Cite news|title=Soviet Pentecostals Meet Illegally and Form Council|last=Special To the New York Times|url=https://www.nytimes.com/1979/09/05/archives/soviet-pentecostals-meet-illegally-and-form-council-20-delegates.html|access-date=2025-12-18|lang=en|archive-url=https://web.archive.org/web/20180310060316/https://www.nytimes.com/1979/09/05/archives/soviet-pentecostals-meet-illegally-and-form-council-20-delegates.html|archive-date=2018-03-10}}
[[श्रेणी:जीवित लोग]]
a1vg1bchqoun06ikl4wrbsvl2pzyuhh
803656
803655
2026-07-30T06:44:27Z
Asorev
38000
/* बाहरी कड़ी */
803656
wikitext
text/x-wiki
{{Infobox person
| name = बोरिस परचात्किन
| native_name = Борис Георгиевич Перчаткин
| image =
| caption =
| birth_date = {{Birth date and age|1946|7|1}}
| birth_place = [[मिर्होरौद]], [[यूक्रेनी सोवियत समाजवादी गणराज्य|यूक्रेनी SSR]], [[सोवियत संघ]]<ref name="ostrovskaja_68">{{cite journal |last=Островская |first=О. П. |year=2017 |title=Судьбы участников эмиграционного движения пятидесятников на дальнем востоке в 1970-1980-х гг. |journal=Владивосток – точка возвращения: прошлое и настоящее русской эмиграции |publisher=Дальневосточный юридический институт МВД РФ |url=http://dot.kostacademy.kz/bible/files/769476832.pdf#page=65 |language=ru |pages=68}}</ref>
| occupation = मानवाधिकार कार्यकर्ता, धार्मिक नेता
| children = 6<ref name="ostrovskaja_68" />
}}
'''बोरिस जोर्जेविच परचात्किन''' (रूसी: ''Борис Георгиевич Перчаткин''; जनम: 1 जुलाई 1946) सोवियत संघ आ अमेरिका के एकठु मानवाधिकार कार्यकर्ता हउवँ आ 1970 के दशक के अंत आ 1980 के दशक के शुरुआत में [[नाखोदका]] शहर से शुरू भइल धार्मिक प्रवास आंदोलन के प्रमुख नेता रहलें<ref name="ostrovskaja_68" />। 1989 में उहाँ [[अमेरिका|संयुक्त राज्य अमेरिका]] में "लाउतेनबर्ग संशोधन" (Lautenberg Amendment) के पास करावे खातिर पैरवी के नेतृत्व कइलीं, जेकरा चलते पूर्व सोवियत संघ के देसवन से लगभग 10 लाख धार्मिक अल्पसंख्यकलोग कानूनी रूप से अमेरिका में जा के बस सकल<ref name="romanovic">{{cite web |last=Романович |first=Михаил |year=2023 |title=Правозащитник, Борис Перчаткин, смертельно болен |website=spasenie.by |date=2023-09-26 |url=https://spasenie.by/publ/pravozashhitnik_boris_perchatkin_smertelno_bolen/4-1-0-82 |language=ru}}</ref>।
== जीवनी ==
=== सुरुआती जीवन आ चर्च में शामिल भइल ===
बोरिस परचात्किन के जनम एक सैन्य पायलट के परिवार में भइल रहे। बोरिस के जनम के कुछ समय बाद ही उनकर पिता के एक कार दुर्घटना में मौत हो गइल। कुछ साल बाद उनकर माई दूसरा सैन्यकर्मी से बियाह क लेली। लेकिन 1953 में उनकर सौतेला बाप के विद्रोह के दौरान हुक्म ना माने के कारण गिरफ्तार क लिहल गइल आ [[वोरकुता]] के जबरन श्रम शिविर में 25 साल के सजा सुनावल गइल<ref name="ostrovskaja_68" />। 16 साल के उमिर में बोरिस नाखोदका में पेन्टेकोस्टल चर्च के खोज कइलें आ ओकर सदस्य बन गइलें। ओकरा बाद उहाँ सोवियत संघ में धार्मिक लोगन पर भइल अत्याचार के बारे में जानकारी जुटावे लगलें आ बिदेसी पत्रकारन से शेयर करे लगलें<ref name="semjonova">{{cite web |last=Семёнова |first=Виктория |year=2020 |title=Борис Перчаткин — о том, как помогал верующим из СССР попасть в США |website=usa.one |date=2020-01-03 |url=https://usa.one/2020/01/boris-perchatkin-o-veruyushhix-v-sssr-immigracii-v-ssha-i-aktivizme/ |language=ru}}</ref>।
=== प्रवास आंदोलन आ असंतुष्टन से संबंध ===
1976 में परचात्किन अपने साथी वासिली पात्रुशेव, वी. ए. बुरलाचेन्को आ व्लादिमीर स्टेपानोव के साथे [[मॉस्को]] में सोवियत असंतुष्टन (dissidents) से संपर्क साधलें<ref name="ostrovskaja_68_69">{{cite journal |last=Островская |first=О. П. |year=2017 |title=Судьбы участников эмиграционного движения пятидесятников на дальнем востоке в 1970-1980-х гг. |journal=Владивосток – точка возвращения: прошлое и настоящее русской эмиграции |publisher=Дальневосточный юридический институт МВД РФ |url=http://dot.kostacademy.kz/bible/files/769476832.pdf#page=65 |language=ru |pages=68—69}}</ref>। असंतुष्टलोग सलाह दिहल कि बढ़त प्रवास आंदोलन के एक संगठित रूप दिहल जाव<ref name="ostrovskaja_68_69" />। ओही साल [[मॉस्को हेलसिंकी ग्रुप]] के प्रतिनिधि एल. वी. वोरोनिना नाखोदका आइली<ref name="ostrovskaja_68_69" />। ओकरा बाद से परचात्किन पेन्टेकोस्टल समुदाय के सोवियत संघ से सामूहिक निकास खातिर सक्रिय रूप से काम कइलें<ref name="ostrovskaja_68_69" />। उहाँ [[संयुक्त राष्ट्र]], [[चर्चन के विश्व परिषद]] (WCC) आ [[हेलसिंकी समझौता]] पर दस्तखत करे वाला देसन के प्रमुखन के अपील भेज के सोवियत संघ में धार्मिक उत्पीड़न के जानकारी दिहलीं<ref name="nashaamerica">{{cite web|url=http://www.nashaamerica.com/blogi/chtivo/item-894/|title=Он спас 1 млн человек. Сегодня они не могут собрать ему на операцию. Как же так?|website=www.nashaamerica.com|date=2023-09-26|access-date=2023-09-26|language=ru|archive-date=2023-09-26|archive-url=https://web.archive.org/web/20230926133215/http://www.nashaamerica.com/blogi/chtivo/item-894/|url-status=live}}</ref>।
1977 में निकोलाई गोरेती के साथे पेन्टेकोस्टल समुदाय के ओर से गिरफ्तार असंतुष्टन [[अलेक्जेंडर गिंज़बर्ग]], [[युरी ओर्लोव]] आ [[अनातोली शचरान्स्की]] के बचाव कइलें<ref name="ostrovskaja_66">{{cite journal |last=Островская |first=О. П. |year=2017 |title=Судьбы участников эмиграционного движения пятидесятников на дальнем востоке в 1970-1980-х гг. |journal=Владивосток – точка возвращения: прошлое и настоящее русской эмиграции |publisher=Дальневосточный юридический институт МВД РФ |url=http://dot.kostacademy.kz/bible/files/769476832.pdf#page=65 |language=ru |pages=66}}</ref>। ओही साल बाकी ईसाई पंथन के साथ मिलकर उत्पीड़ित ईसाइयन खातिर देश छोड़े के इजाजत माँगे खातिर सार्वजनिक भूख हड़ताल में भाग लिहलें<ref name="ukrweekly">{{cite journal|author=Zenon Snylyk|title=Dissidents fast across USSR|url=https://archive.ukrweekly.com/print-media/1977/The_Ukrainian_Weekly_1977-38.pdf|journal=The Ukrainian Weekly|publisher=Ukrainian National Association|date=1977-10-09|volume=LXXXIV|issue=222|pages=2|language=en|archive-date=2023-09-26|archive-url=https://web.archive.org/web/20230926074510/https://archive.ukrweekly.com/print-media/1977/The_Ukrainian_Weekly_1977-38.pdf|url-status=live}}</ref>।
20 दिसंबर 1978 के परचात्किन नाखोदका के डाकघर से अमेरिकी राष्ट्रपति [[जिमी कार्टर]] के नया साल के टेलीग्राम भेजे के कोशिश कइलें, जेमे धार्मिक आजादी से वंचित लोगन पर ध्यान देवे के बिनती कइल गइल रहे<ref name="ostrovskaja_69">{{cite journal |last=Островская |first=О. П. |year=2017 |title=Судьбы участников эмиграционного движения пятидесятников на дальнем востоке в 1970-1980-х гг. |journal=Владивосток – точка возвращения: прошлое и настоящее русской эмиграции |publisher=Дальневосточный юридический институт МВД РФ |url=http://dot.kostacademy.kz/bible/files/769476832.pdf#page=65 |language=ru |pages=69}}</ref><ref name="nashaamerica"/><ref name="dementjev">{{cite book |author=Андрей Дементьев |title=АВЕН-ЕЗЕР. Евангельское движение в Приморье 1898-1990 годы |url=http://www.rusbaptist.stunda.org/aven-ezer-primorje.pdf |location=Vladivostok |publisher=Русский остров |year=2011 |pages=213 |isbn=978-5-93577-054-2 |language=ru |archive-date=2023-09-25 |archive-url=https://web.archive.org/web/20230925212100/http://www.rusbaptist.stunda.org/aven-ezer-primorje.pdf |url-status=live}}</ref>।
1979 से उहाँ देस छोड़े के इच्छुक लोगन खातिर एक केंद्र बनाए के संगठनात्मक काम के नेतृत्व कइलें। 1980 में उहाँ के नवगठित "सोवियत पेन्टेकोस्टल चर्च काउंसिल फॉर इमिग्रेशन" के सचिव चुनल गइल<ref name="ostrovskaja_69" />।
=== सरकारी उत्पीड़न आ जेल ===
प्रवास समिति के सबसे सक्रिय सदस्य भइला के कारण उहाँ सोवियत अधिकारियों के निशाना बन गइलें<ref name="ostrovskaja_69" />। स्थानीय मीडिया में उनुका आ उनकर परिवार के खिलाफ बदनामी के अभियान चलावल गइल<ref name="ostrovskaja_69" />।
21 अगस्त 1980 के परचात्किन के नाखोदका में गिरफ्तार क लिहल गइल<ref name="ostrovskaja_69" />। मार्च 1981 के अंत में<ref name="dtic">[https://apps.dtic.mil/sti/pdfs/ADA145453.pdf Chronology of Soviet Dissidence: January 1970 through December 1982]</ref>, आरएसएफएसआर आपराधिक संहिता के अनुच्छेद 190.1 ("सोवियत राज्य आ सामाजिक व्यवस्था के बदनाम करे वाला झूठ फइलावे") के तहत उनुका के 2 साल के जेल के सजा सुनावल गइल<ref name="dtic"/>। [[वॉइस ऑफ अमेरिका]] के रिपोर्ट के मुताबिक, पूछताछ आ ट्रायल के दौरान उनुका के न्यूरोलेप्टिक रसायन दिहल गइल जेकरा से उहाँ कई बेर बेहोश हो गइलें<ref name="ostrovskaja_69" />।
अगस्त 1982 में रिहा भइला के बाद उहाँ देश छोड़े के अधिकार खातिर लड़ाई जारी रखलें। लेकिन फरवरी 1983 में उनुका के दोबारा गिरफ्तार क लिहल गइल आ अवैध हथियार रखे के झूठा आरोप में 1.5 साल के जेल के सजा दिहल गइल<ref name="ostrovskaja_69" />।
=== अमेरिका में प्रवास आ लाउतेनबर्ग संशोधन ===
1987 में उहाँ अमेरिकी कांग्रेस खातिर "सोवियत संघ में धर्म के स्थिति" पर एक रिपोर्ट तैयार कइलें। ई रिपोर्ट कांग्रेस में पढ़ल गइल आ परचात्किन के अमेरिका के विदेश मंत्री [[जॉर्ज शुल्त्ज़]]<ref name="romanovic"/> आ मुख्य न्यायाधीश [[विलियम रेनक्विस्ट]]<ref name="romanovic"/> से मिले खातिर बुलावल गइल। मई 1988 में जब अमेरिकी राष्ट्रपति [[रोनाल्ड रीगन]] आ सोवियत नेता [[मिखाइल गोर्बाचेव]] मॉस्को में शिखर सम्मेलन कइलें, तब परचात्किन के सोवियत प्रोटेस्टेंट प्रतिनिधि के रूप में फर्स्ट लेडी [[नैंसी रीगन]] से मिले खातिर 40 लोगन के प्रतिनिधिमंडल में आमंत्रित कइल गइल रहे<ref name="gura_173">{{cite journal |last=Гура |first=В. О. |year=2018 |title=Історія та есхатологічні мотиви релігійної еміграції пізніх протестантів із СРСР 1987–2017 рр. |journal=Гілея: науковий вісник |issue=135 |pages=171—175 |issn=2076-1554 |url=http://www.irbis-nbuv.gov.ua/cgi-bin/irbis_nbuv/cgiirbis_64.exe?C21COM=2&I21DBN=UJRN&P21DBN=UJRN&IMAGE_FILE_DOWNLOAD=1&Image_file_name=PDF/gileya_2018_135_47.pdf |language=uk}}</ref>। ओह बैठक में उहाँ धार्मिक आजादी आ बिना कौनों बाधा के देस छोड़े के अधिकार खातिर अपील सउंपलें।
26 जुलाई 1988 के परचात्किन अपना पूरा परिवार के साथ अमेरिका चले गइलें<ref name="gura_173" /><ref name="ostrovskaja_69" />। 4 अगस्त 1988 के उहाँ अमेरिकी कांग्रेस में सोवियत संघ में धार्मिक उत्पीड़न पर भाषण दिहले<ref name="gura_173" /><ref name="ostrovskaja_69" /><ref name="photo">{{cite web|url=https://russiainphoto.ru/photos/195013/|title=Foto - Boris Perchatkin|website=russiainphoto.ru|date=2023-09-26|language=ru}}</ref>। एही प्रयासन के बदौलत<ref name="futornyj">{{cite journal|author=Виталий Футорный|title=25-летие начала исхода|url=https://issuu.com/afisha/docs/diaspora_08-11-13/9|journal=Диаспора. Славянская общественная газета|date=2013-08-11|volume=16|issue=15|pages=9|language=ru}}</ref> 1989 में "लाउतेनबर्ग संशोधन" पास भइल, जेकर नाम [[न्यू जर्सी]] के सीनेटर [[फ्रैंक लाउतेनबर्ग]] के नाम पर रखल गइल रहे। एह कानून के पहिला 10 साल में लगभग 3,50,000 धार्मिक शरणार्थी अमेरिका में बसलें<ref name="gura_173" /><ref name="semjonova" />, आ 2010 तक ई संख्या लगभग 10 लाख तक पहुँच गइल<ref name="romanovic"/>।
=== अमेरिका में मानवाधिकार गतिविधि ===
अमेरिका में रहत समय परचात्किन अपना जेल आ संघर्ष के जीवन पर एक वेबसाइट शुरू कइलें आ एक ईसाई मानवाधिकार संगठन ARRC (American-Russian Relief Committee) के स्थापना कइलें<ref name="ostrovskaja_69" /><ref name="romanovic"/><ref name="arrc">{{cite book|author=United States Congress Commission on Security and Cooperation in Europe|title=Unregistered Religious Groups in Russia: Hearing Before the Commission on Security and Cooperation in Europe|url=https://books.google.com/books?id=_YhIiVBx_5kC|year=2007|publisher=U.S. Government Printing Office|isbn=978-0-16-077910-7|language=en}}</ref>। उहाँ अपना आत्मकथात्मक किताब "आग के रास्ते" (रूसी: ''Огненные тропы'') प्रकाशित कइलें<ref name="ostrovskaja_69" />।
2025 के गरमी में परचात्किन आ उनकर समर्थक अमेरिकी विदेश विभाग आ अंतर्राष्ट्रीय धार्मिक स्वतंत्रता पर अमेरिकी आयोग (USCIRF) के सामने गवाही दिहलें, जेमे यूक्रेन आ रूस में शांतिवादी ईसाइयन के उत्पीड़न आ जबरन सेना में भर्ती के मुद्दा उठावल गइल। एह सिलसिले में उहाँ [[Uniting for Ukraine|U4U]] कार्यक्रम के तहत अमेरिकी आवे वाला यूक्रेनी शांतिवादी ईसाइयन खातिर विशेष कोटा खातिर पैरवी कइलें<ref name="slavicsac">{{cite web|url=https://www.slavicsac.com/2025/07/14/ukraine-russian-christians/|title=В США обсуждают защиту христиан из Украины и России|author=SlavicSac|website=Slavic Sacramento|date=2025-07-15|language=ru}}</ref>।
== सम्मान आ उल्लेख ==
प्रसिद्ध सोवियत असंतुष्ट [[आंद्रेई साखारोव]] 1981 में लिखे गइल अपना निबंध "वैज्ञानिकन के जिम्मेदारी" में राजनीतिक बंदियों के सूची में बोरिस परचात्किन के नाम के विशेष रूप से उल्लेख कइले रहलें<ref name="sakharov">{{cite web|url=https://www.sakharov.space/lib/otvetstvennost-uchenyh|title=Сахаров. Ответственность учёных|website=sakharov.space|language=ru}}</ref>।
== संदर्भ ==
<references />
== बाहरी कड़ी ==
* {{Cite journal
|author = Michael Rowe
|title = The Soviet pentecostal emigration movement
|url = https://www.tandfonline.com/doi/epdf/10.1080/09637498308431098
|journal = Religion in Communist Lands
|language = en
|date = 2 January 2008
|volume = 11
|issue = 3
|pages = 337–339
|doi = 10.1080/09637498308431098
|ref = Rowe
|url-access= subscription }}
* {{Cite journal
|author = Catherine Wanner
|title = Religion and Refugee Resettlement: Evolving Connections to Ukraine since World War II
|url = https://brill.com/view/journals/css/44/1-2/article-p44_4.xml
|journal = Canadian-American Slavic Studies
|language = en
|publisher = Brill Schöningh
|date = January 2010
|volume = 44
|issue = 1–2
|pages = 44–66
|issn = 2210-2396
|doi = 10.1163/221023910X512796
|ref = Wanner
|url-access= subscription}}
* {{Cite news|title=Soviet Pentecostals Meet Illegally and Form Council|last=Special To the New York Times|url=https://www.nytimes.com/1979/09/05/archives/soviet-pentecostals-meet-illegally-and-form-council-20-delegates.html|access-date=2025-12-18|lang=en|archive-url=https://web.archive.org/web/20180310060316/https://www.nytimes.com/1979/09/05/archives/soviet-pentecostals-meet-illegally-and-form-council-20-delegates.html|archive-date=2018-03-10}}
{{Authority control}}
[[श्रेणी:जीवित लोग]]
dvmt2728n4ar3ru847kl70qpkvdkwxw